Question

I am trying to grab the node value of an external document, but am nowhere near getting anything that works.

This is the products.xml

<TESTS>
<CHILD>
     <KEY>111111</KEY>
     <PRODUCT>Toy 1</PRODUCT>
    </CHILD>
    <CHILD>
     <KEY>222222</KEY>
     <PRODUCT>Toy 2</PRODUCT>
    </CHILD>
</TESTS>

This is the file being transformed companys.xml

<INVENTORY>
     <PRODUCTS>
     <ITEMS>
          <ITEM TYPE="Toys">111111</ITEM>
     </ITEMS>
 </PRODUCTS>
     <PRODUCTS>
     <ITEMS>
          <ITEM TYPE="Toys">222222</ITEM>
     </ITEMS>
 </PRODUCTS>
<INVENTORY>

This is the xsl code I have

<xsl:variable name="lookupDoc" select="document('products.xml')" />
<xsl:key name="product-lookup" match="KEY" use="."/>

<xsl:template match="PRODUCT_HTML">
<xsl:value-of select="./@TYPE"/> <xsl:value-of select="."/> <xsl:value-of select="$lookupDoc/exam-lookup" />
</xsl:template>

What I am getting from this is

Toys 111111

What I would Like to get is

Toys 111111 Toy 1

Can you please help me out?

Thanks,

Alex

Was it helpful?

Solution

The xsl:key element has to be used in conjunction with the key() function.

Your key definition

<xsl:key name="product-lookup" match="KEY" use="."/>

is not quite what you want, I think. match controls the element that you want to return, while use tells what expression is being matched in the lookup. As written, it will return the KEY element itself, and to get to the PRODUCT you'd have to traverse laterally. It probably should be

<xsl:key name="product-lookup" match="CHILD" use="KEY"/>

This way, an expression like

<xsl:value-of select="key('product-lookup', '111111') />

will point to the first CHILD node in the products.xml file. Then you can traverse downward to the product name.

However, there's a wrinkle in XSLT 1.0 when using the key() function on an external document: key() operates on the "context document", so if the current node is in the input document companys.xml, the lookup will not give the result you want.

To work around this, you can set the context to the target document, as follows:

<xsl:variable name="key" select="." />
<xsl:variable name="child">
    <xsl:for-each select="$lookupDoc">
        <xsl:value-of select="key('product-lookup', $key)/PRODUCT"/>
    </xsl:for-each>
</xsl:variable>

Now $child now contain the string value of the PRODUCT element under the corresponding CHILD. (See comments re variable binding.)

Note that an extra variable is needed to store the key so that . doesn't change its meaning when you change the context. In other words, you have to save a binding to the key value from the original document first.

In XSLT 2.0, you can avoid some of that by using a third parameter that was added to the key() function for setting the context document:

<xsl:variable name="child" select="key('product-lookup', ., $lookupDoc)" />

Makes this (common) problem much cleaner.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top