Search code examples
xslt-3.0xslkey

using xsl:key to extract elements xslt 3


Given this XML:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE pm [
]>
    <pm>
    <content>
        <pmEntry>
     <dmRef>
        <dmRefIdent>
           <dmCode modelIdentCode="S1000DBIKE"
                   systemDiffCode="AAA"
                   systemCode="D00"
                   subSystemCode="0"
                   subSubSystemCode="0"
                   assyCode="00"
                   disassyCode="00"
                   disassyCodeVariant="AA"
                   infoCode="001"
                   infoCodeVariant="A"
                   itemLocationCode="A"/>
           <issueInfo issueNumber="001" inWork="00"/>
           <language countryIsoCode="US" languageIsoCode="en"/>
        </dmRefIdent>
        <dmRefAddressItems>
           <dmTitle>
              <infoName>Title page</infoName>
           </dmTitle>
           <issueDate day="31" month="12" year="2012"/>
        </dmRefAddressItems>
     </dmRef>
  </pmEntry>
  <pmEntry>
     
     <dmRef>
        <dmRefIdent>
           <dmCode assyCode="00" disassyCode="00" disassyCodeVariant="E01" infoCode="130"
              infoCodeVariant="D" itemLocationCode="A" modelIdentCode="555"
              subSubSystemCode="0" subSystemCode="3" systemCode="15" systemDiffCode="A"/>
        </dmRefIdent>
        <dmRefAddressItems>
           <dmTitle>
              <infoName>Pre-Flight</infoName>
           </dmTitle>
        </dmRefAddressItems>
     </dmRef>
     <dmRef>
        <dmRefIdent>
           <dmCode assyCode="00" disassyCode="00" disassyCodeVariant="E02" infoCode="130"
              infoCodeVariant="D" itemLocationCode="A" modelIdentCode="525"
              subSubSystemCode="0" subSystemCode="3" systemCode="15" systemDiffCode="A"/>
        </dmRefIdent>
        <dmRefAddressItems>
           <dmTitle>
              <infoName>Post-Flight</infoName>
           </dmTitle>
        </dmRefAddressItems>
     </dmRef>
     <dmRef>
        <dmRefIdent>
           <dmCode modelIdentCode="FLRA" systemDiffCode="A" systemCode="00" subSystemCode="0" subSubSystemCode="0" 
              assyCode="00" disassyCode="00" disassyCodeVariant="E01" infoCode="131" infoCodeVariant="T" itemLocationCode="A"/>
        </dmRefIdent>
        <dmRefAddressItems>
           <dmTitle>
               <infoName>Through-Flight Checklist</infoName>
           </dmTitle>
        </dmRefAddressItems>
     </dmRef>
     </pmEntry>
  <pmEntry>
     <dmRef>
        <dmRefIdent>
           <dmCode assyCode="00" disassyCode="11" disassyCodeVariant="A01" infoCode="141"
              infoCodeVariant="A" itemLocationCode="A" modelIdentCode="555"
              subSubSystemCode="0" subSystemCode="3" systemCode="15" systemDiffCode="A"/>
        </dmRefIdent>
        <dmRefAddressItems>
           <dmTitle>
              <infoName>First Advisory</infoName>
           </dmTitle>
        </dmRefAddressItems>
     </dmRef>
     <dmRef>
        <dmRefIdent>
           <dmCode assyCode="00" disassyCode="15" disassyCodeVariant="A03" infoCode="141"
              infoCodeVariant="A" itemLocationCode="A" modelIdentCode="555"
              subSubSystemCode="9" subSystemCode="4" systemCode="15" systemDiffCode="A"/>
        </dmRefIdent>
        <dmRefAddressItems>
           <dmTitle>
              <infoName>Status Messages</infoName>
           </dmTitle>
        </dmRefAddressItems>
     </dmRef>
     <dmRef>
        <dmRefIdent>
           <dmCode assyCode="00" disassyCode="20" disassyCodeVariant="A99" infoCode="141"
              infoCodeVariant="A" itemLocationCode="A" modelIdentCode="555"
              subSubSystemCode="0" subSystemCode="3" systemCode="15" systemDiffCode="A"/>
        </dmRefIdent>
        <dmRefAddressItems>
           <dmTitle>
              <infoName>Last Advisory</infoName>
           </dmTitle>
        </dmRefAddressItems>
     </dmRef>
  </pmEntry>
  <pmEntry>
    <dmRef>
        <dmRefIdent>
           <dmCode assyCode="00" disassyCode="01" disassyCodeVariant="C99" infoCode="141" infoCodeVariant="A"
              itemLocationCode="A" modelIdentCode="555" subSubSystemCode="0" subSystemCode="4"
              systemCode="15" systemDiffCode="A"/>
        </dmRefIdent>
        <dmRefAddressItems>
           <dmTitle>
              <infoName>Cautions</infoName>
           </dmTitle>
        </dmRefAddressItems>
     </dmRef>
     <dmRef>
        <dmRefIdent>
           <dmCode assyCode="00" disassyCode="10" disassyCodeVariant="C99" infoCode="141"
              infoCodeVariant="A" itemLocationCode="A" modelIdentCode="555" subSubSystemCode="0"
              subSystemCode="4" systemCode="15" systemDiffCode="A"/>
        </dmRefIdent>
        <dmRefAddressItems>
           <dmTitle>
              <infoName>Cautions End</infoName>
           </dmTitle>
        </dmRefAddressItems>
     </dmRef>
  </pmEntry>
    </content>
    </pm>

I wanted to rewrite the following with a key (this does work):

<xsl:variable name="advisory-DMs" as="item()*">
        <xsl:copy-of select="pmEntry//dmRef/dmRefIdent/dmCode[@infoCode=('141') and @infoCodeVariant='A' and starts-with(@disassyCodeVariant,'A')]"/> 
</xsl:variable>

So I tried variations of:

<xsl:key name="kICV" match="dmCode" use="concat(@infoCode, @infoCodeVariant)" />
<xsl:variable name="advisory-DMs" as="item()*">
        <xsl:copy-of select="pmEntry//dmRef/dmRefIdent/dmCode[key('kICV','141A') and starts-with(@disassyCodeVariant,'A')]"/> 
</xsl:variable>

But the variable contains the first dmCode where infoCode="001" so it's returning all dmCodes where @disassyCodeVariant starts with 'A' and ignoring @infoCode='141'. I'm looking for a more efficient way of writing this, maybe I need two keys? If I want to search on @disassyCode as well would I need three keys?


Solution

  • First of all, in XSLT 3, you don't need the concat of several key values, you can just ensure the key value is a sequence of several values with composite e.g.

    <xsl:key name="kICV" match="dmCode" composite="yes" use="@infoCode, @infoCodeVariant" />
    

    Then I think you just want

    <xsl:variable name="advisory-DMs" select="key('kICV', ('141', 'A'))[starts-with(@disassyCodeVariant,'A')]"/>
    

    Note that the key function in XSLT 2 and higher allows an optional third argument to provide the document node or subtree to search. It is not clear from your description whether you work with a single document or more so that you would need to pass in the third argument to determine the document or whether you want the key search to be restricted to a certain subtree you would also need to pass in (e.g. <xsl:variable name="advisory-DMs" select="key('kICV', ('141', 'A'), pmEntry)[starts-with(@disassyCodeVariant,'A')]"/>).