2

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.

MTS
  • 1,845
  • 2
  • 17
  • 18

1 Answers1

5

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"/>
Dimitre Novatchev
  • 240,661
  • 26
  • 293
  • 431
  • Wow! XSLT continues to amaze me with how powerful it is. This has literally taken a process that was taking 5+ hours down to just seconds. Thank you Dimitre! – MTS Feb 14 '13 at 23:30
  • @MTS, You are welcome. And yes, XSLT is both powerful and elegant -- not to mention XSLT 2.0 and now the forthcoming XSLT 3.0. – Dimitre Novatchev Feb 15 '13 at 00:13