Nick Fedorowicz
Nick Fedorowicz

Reputation: 23

XSLT 1.0 Value look up Map

I am new to XSLT and spent a fair amount of time getting to grips with creating an in-line look up map to replace a specific value with another value for a mapped list in XSLT 2.0 only to find out I can only use 1.0. :-(

My question is how can a replicate the below working XSLT 2.0 code in 1.0. I have tried a few things but can't seem to get it working.

Point to note that if there is no map then the element should be empty.

    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
    <xsl:output omit-xml-declaration="yes" indent="yes"/>
    <xsl:template match="node()|@*">
    <xsl:copy>
        <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
    </xsl:template>


<xsl:variable name="mapxml" >
<map>
<Country>
    <input value="GB">RZ</input>
    <input value="FR">TH</input>
   </Country>
</map>
 </xsl:variable>


  <xsl:variable name="vMap" 
       select="$mapxml" />


 <xsl:key name="kInputByVal" match="input" 
   use="@value" />


    <xsl:template match="Country/text()">
        <xsl:sequence select= 
         "(key('kInputByVal', ., $vMap/*/Country)[1]/text()
           )[1]
         "/>

 </xsl:template>
</xsl:stylesheet>

Input XML:

 <user>
        <Country>GB</Country>
        <Name>FOO</Name>
        <Address>BAR</Address>
<user>

Upvotes: 2

Views: 5111

Answers (2)

michael.hor257k
michael.hor257k

Reputation: 116992

Keys in XSLT 1.0 work only in the current document. In order to use a key to lookup from the stylesheet itself, you must switch the context to the stylesheet before using the key:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="http://example.com/my"
exclude-result-prefixes="my">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:key name="input-by-value" match="input" use="@value" />

<my:map>
    <input value="GB">RZ</input>
    <input value="FR">TH</input>
</my:map>

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

<xsl:template match="Country/text()">
    <xsl:variable name="value" select="." />
    <!-- switch context to stylesheet in order to use key -->
    <xsl:for-each select="document('')">
        <xsl:value-of select="key('input-by-value', $value)"/>
    </xsl:for-each>
 </xsl:template>

</xsl:stylesheet>

Upvotes: 2

Tomalak
Tomalak

Reputation: 338208

Here is the equivalent XSLT 1.0 program:

<xsl:stylesheet version="1.0" 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:my="http://tempuri.org/dummy"
  exclude-result-prefixes="my"
>
  <xsl:output omit-xml-declaration="yes" indent="yes"/>
  <xsl:strip-space elements="*" />

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

  <my:config>
    <map>
      <Country>
        <input value="GB">RZ</input>
        <input value="FR">TH</input>
      </Country>
    </map>
  </my:config>
  <xsl:variable name="vCountryMap" select="document('')/*/my:config/map/Country/input" />

  <xsl:template match="Country/text()">
    <xsl:value-of select="$vCountryMap[@value = current()]" />
  </xsl:template>
</xsl:stylesheet>

Notes:

  • You can set up extra nodes within the XSLT, simply because XSLT is XML itself. For example for configuration data, like here. You just need to make sure that you use a different namespace for them.
  • Namespace URIs need to be unique, they don't need to point to an existing document. A temporary URI like http://tempuri.org/dummy is perfectly all-right.
  • <xsl:strip-space elements="*" /> instructs the XSLT processor to ignore insignificant whitespace in the input. This helps in creating neatly indented output.
  • document('') refers to the XSLT stylesheet itself. You could also keep the configuration in an extra XML file, if you wanted to.
  • exclude-result-prefixes prevents the leaking of our temp namespace to the output.
  • current() refers to the node the XSLT processor is currently handling. ($vCountryMap[@value = .] would not work, because here the . refers to the XPath context, i.e. the <input> nodes.)

Upvotes: 4

Related Questions