Question

We have a program that uses xml to save configurations of our program. Someone decided to rename a couple of values in our database and these renames should now also be backwards compatible in the configurations of our customers.

An example of a configuration

<configuration>
    <fruitToEat>yellow_curved_thing</fruitToEat> <!-- should now become banana -->
</configuration>

A simple match would be (not tested, just an example):

<xsl:template>
  <xsl:template match="node()|@*">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match"/configuration/fruitToEat/text()">
    <xsl:text>banana</xsl:text>
  </xsl:template>
</xsl:template>

But this is just one example and I want to do this 150 times.

Is it possible to make an xsl that reads a simple text file or ini file that tells me how the 150 matches should look alike?

<xsl:template>
  <xsl:template match="node()|@*">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
  </xsl:template>

  <!-- recreate this template 150 times from an ini file or something -->
  <xsl:template match"/configuration/fruitToEat/text()[.='yellow_curved_thing']">
    <xsl:text>banana</xsl:text>
  </xsl:template>
</xsl:template>

An example of my mapping file could be simply:

yellow_curved_thing = banana
round_thing = tomato
round_dotted = strawberry

And I would simply want a small xslt that tells me:

<xsl:template>
  <xsl:template match="node()|@*">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
  </xsl:template>

  <!-- recreate this template 150 times from an ini file or something -->
  <xsl:template match"/configuration/fruitToEat/text()[.=$fileRow0]">
    <xsl:text>$fileRow1</xsl:text>
  </xsl:template>
</xsl:template>
Was it helpful?

Solution

So even if I think there is more complexity behind the curtain, may be this will help a little bit. There are some possibilities to do this with xlst. Which would be best in long term depends on complexity in real life and how often you need to do this with different "mapping" information.

For your easy example you can put the "mapping" information into a xml file. This could be done by some script form ini file.

<mappings>
    <mapping name="fruitToEat" >
        <map  from="yellow_curved_thing" to="banana"  />
        <map  from="round_thing" to="tomato"  />
        <map  from="round_dotted" to="strawberry"  />
    </mapping>
</mappings>

Than you can have a template which make use of this mapping information:

<xsl:variable name="fruitMapping"
             select="document('fruitmapping.xml')//mapping[@name='fruitToEat']" />

    <xsl:template match="node()|@*">
            <xsl:copy>
                <xsl:apply-templates select="node()|@*"/>
            </xsl:copy>
        </xsl:template>

    <xsl:template match="/configuration/fruitToEat/text()"  >
        <!-- find the entry in "ini file" -->
        <xsl:variable name ="map" select="$fruitMapping/map[@from = current()]" />
        <xsl:choose>
            <xsl:when test="$map" >
                <xsl:value-of select="$map/@to"/>
            </xsl:when>
            <xsl:otherwise>
                <xsl:copy />
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

But if this is a onetime job I would implement this "mapping" direct a template. Like this:

<xsl:template match="/configuration/fruitToEat/text()" >
    <xsl:choose>
        <xsl:when test=".='yellow_curved_thing'" >banana</xsl:when>
        <xsl:when test=".='round_thing'" >tomato</xsl:when>
        <xsl:when test=".='round_dotted'" >strawberry</xsl:when>
        <xsl:otherwise>
            <xsl:copy />
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

OTHER TIPS

It seems you wanted to create your XSLT dynamic XSLT on the basis of your configuration file which is also an XML document. Have a look this exmple considering this:

configuration.xml

    <p>
  <configuration>
    <fruitToEat>yellow_curved_thing</fruitToEat>
    <mapped>banana</mapped>
  </configuration>
  <configuration>
    <fruitToEat>round_thing</fruitToEat>
    <mapped>tomato</mapped>
  </configuration>
</p>

XSLT:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

  <xsl:output method="xml" encoding="UTF-8" indent="yes" omit-xml-declaration="yes"/>

  <xsl:template match="/">
    <xsl:element name="xsl:stylesheet">
      <xsl:attribute name="version">
        <xsl:text>1.0</xsl:text>
      </xsl:attribute>
      <xsl:element name="xsl:template">
        <xsl:attribute name="match">
          <xsl:text>node()|@*</xsl:text>
        </xsl:attribute>
        <xsl:element name="xsl:copy">
          <xsl:element name="xsl:apply-templates">
            <xsl:attribute name="select">
              <xsl:text>node()|@*</xsl:text>
            </xsl:attribute>
          </xsl:element>
        </xsl:element>
      </xsl:element>
      <xsl:for-each select="//configuration">
        <xsl:element name="xsl:template">
          <xsl:attribute name="match">
            <xsl:text>configuration/fruitToEat/text()[.=</xsl:text>
            <xsl:text>'</xsl:text>
            <xsl:value-of select="fruitToEat"/>
            <xsl:text>']</xsl:text>
          </xsl:attribute>
          <xsl:element name="xsl:text">
              <xsl:value-of select="mapped"/>
          </xsl:element>
        </xsl:element>
      </xsl:for-each>
    </xsl:element>
  </xsl:template>
</xsl:stylesheet>

output:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
   <xsl:template match="node()|@*">
      <xsl:copy>
         <xsl:apply-templates select="node()|@*"/>
      </xsl:copy>
   </xsl:template>
   <xsl:template match="configuration/fruitToEat/text()[.='yellow_curved_thing']">
      <xsl:text>banana</xsl:text>
   </xsl:template>
   <xsl:template match="configuration/fruitToEat/text()[.='round_thing']">
      <xsl:text>tomato</xsl:text>
   </xsl:template>
</xsl:stylesheet>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top