I'm trying to use the XSLT key() function to return all the <Code>
elements in an XML file that match the following two criteria:
Code[code=$code] AND ancestor::CodeType[type=$codeType]`
Here is a simplified example of what the input XML looks like:
<Items>
<Item code-number="C1" category="ABC" />
<Item code-number="C3" category="ABC" />
<Item code-number="C1" category="XYZ" />
</Items>
<CodeTypes>
<CodeType type="ABC">
<SubType title="Category III Codes"> <!-- <SubType> elements are optional -->
<SubType title="Subcategory III-15 Codes">
<Code code="C1" description="Red" />
<Code code="C2" description="Green" />
<Code code="C3" description="Blue" />
<Code code="C3" description="Purple" /> <!-- Same code can appear more than once -->
</SubType>
</SubType>
<CodeType>
<CodeType type="XYZ">
<Code code="C1" description="Black" /> <!-- Same code can be used for multiple CodeTypes -->
<Code code="C2" description="Orange" />
<Code code="C3" description="Yellow" />
<CodeType>
</CodeTypes>
Note that the comments aren't actually there in the actual XML, I'm just adding them here to clarify the XML structure.
Here is the XSLT transform I am trying to use, though it doesn't seem to be working:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="LookupMatchingCodeElements" match="Code" use="concat(../@code, '+', ancestor::CodeType/@type)" />
<xsl:template match="Item">
<xsl:call-template name="GetCodeElements">
<xsl:with-param name="code" select="@code-number" />
<xsl:with-param name="codeType" select="@category" />
</xsl:call-template>
</xsl:template>
<xsl:template name="GetCodeElements">
<xsl:param name="code" />
<xsl:param name="codeType" />
<xsl:for-each select="key('LookupMatchingCodeElements', concat($code, '+', $codeType))">
<!-- process each <Code> element -->
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
And this is what I want the key() function to return with different inputs:
<!-- For code="C1" AND codeType="ABC" -->
<Code code="C1" description="Red" />
<!-- For code="C3" AND codeType="ABC" -->
<Code code="C3" description="Blue" />
<Code code="C3" description="Purple" />
<!-- For code="C1" AND codeType="XYZ" -->
<Code code="C1" description="Black" />
Is this possible with the key() function? As there are hundreds of thousands of both <Item>
and <Code>
elements, being able to use <xsl:key>
is very important.
Use:
<xsl:key name="kCode" match="Code"
use="concat(ancestor::CodeType[1]/@type, '+', @code)"/>
Here is a complete transformation:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:key name="kCode" match="Code"
use="concat(ancestor::CodeType[1]/@type, '+', @code)"/>
<xsl:template match="/">
<xsl:copy-of select="key('kCode', 'ABC+C1')"/>
====================
<xsl:copy-of select="key('kCode', 'ABC+C3')"/>
====================
<xsl:copy-of select="key('kCode', 'XYZ+C1')"/>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the following document (the provided XML text -- corrected malformedness):
<t>
<Items>
<Item code-number="C1" category="ABC" />
<Item code-number="C3" category="ABC" />
<Item code-number="C1" category="XYZ" />
</Items>
<CodeTypes>
<CodeType type="ABC">
<SubType title="Category III Codes">
<!-- <SubType> elements are optional -->
<SubType title="Subcategory III-15 Codes">
<Code code="C1" description="Red" />
<Code code="C2" description="Green" />
<Code code="C3" description="Blue" />
<Code code="C3" description="Purple" />
<!-- Same code can appear more than once -->
</SubType>
</SubType>
</CodeType>
<CodeType type="XYZ">
<Code code="C1" description="Black" />
<!-- Same code can be used for multiple CodeTypes -->
<Code code="C2" description="Orange" />
<Code code="C3" description="Yellow" />
</CodeType>
</CodeTypes>
</t>
the wanted, correct result is produced:
<Code code="C1" description="Red"/>
====================
<Code code="C3" description="Blue"/>
<Code code="C3" description="Purple"/>
====================
<Code code="C1" description="Black"/>