Select repeating node with xpath

1.4k views Asked by At

I am using XSLT 1.0 I have the following xml document

<ns0:Root xmlns:ns0="http://schemas.microsoft.com/BizTalk/2003/aggschema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <InputMessagePart_0>
    <ns1:ArrayOfArticleMasterDTO xmlns:ns1="http://BTS.GO.FactFeeds/DC_ArticleMaster">
      <ArticleMasterDTO>
        <AltBarcodes>
          <string>5020436473709</string>
        </AltBarcodes>
        <ClientId>GO01</ClientId>
        <ArticleID>100000005503</ArticleID>
        <ArticleReference>CORE_OWBA_WW873R</ArticleReference>
        <ArticleSize>14</ArticleSize>
        <ArticleStyle>26600</ArticleStyle>
        <ArticleFit>-</ArticleFit>
        <SupplierID>50102</SupplierID>
        <Description>AMELIE OTRS [REG]</Description>
        <Sku>COREOWBAWW873RBLAC-14</Sku>
        <UnitsPerCase>1</UnitsPerCase>
        <UnitsPerLayer xsi:nil="true" />
        <UnitsPerPallet xsi:nil="true" />
        <LayersPerPallet xsi:nil="true" />
        <FullPalletWeightKG xsi:nil="true" />
        <CaseLengthMM xsi:nil="true" />
        <CaseHeightMM xsi:nil="true" />
        <CaseDepthMM xsi:nil="true" />
        <CaseWeightG xsi:nil="true" />
        <Colour>BLAC</Colour>
        <Level1Description>CLOTHING</Level1Description>
        <Level2Description>WOMENS</Level2Description>
        <Level3Description>WP LEGWEAR</Level3Description>
        <Level4Description>LEG COATED</Level4Description>
        <PrimaryBarcode>5051513724896</PrimaryBarcode>
      </ArticleMasterDTO>
      <ArticleMasterDTO>
        <AltBarcodes>
          <string>5027793433728</string>
        </AltBarcodes>
        <ClientId>GO01</ClientId>
        <ArticleID>100000032177</ArticleID>
        <ArticleReference>CORE_OMOE_47354</ArticleReference>
        <ArticleSize>L-XL</ArticleSize>
        <ArticleStyle>24608</ArticleStyle>
        <ArticleFit>-</ArticleFit>
        <SupplierID>50013</SupplierID>
        <Description>POWER STRETCH GLOVE</Description>
        <Sku>COREOMOE47354BLAC-L-XL</Sku>
        <UnitsPerCase>6</UnitsPerCase>
        <UnitsPerLayer xsi:nil="true" />
        <UnitsPerPallet xsi:nil="true" />
        <LayersPerPallet xsi:nil="true" />
        <FullPalletWeightKG xsi:nil="true" />
        <CaseLengthMM xsi:nil="true" />
        <CaseHeightMM xsi:nil="true" />
        <CaseDepthMM xsi:nil="true" />
        <CaseWeightG xsi:nil="true" />
        <Colour>BLAC</Colour>
        <Level1Description>CLOTHING</Level1Description>
        <Level2Description>MENS</Level2Description>
        <Level3Description>GLOVES</Level3Description>
        <Level4Description>FLEECE GLOVE</Level4Description>
        <PrimaryBarcode>5052071278609</PrimaryBarcode>
      </ArticleMasterDTO>
    </ns1:ArrayOfArticleMasterDTO>
  </InputMessagePart_0>
  <InputMessagePart_1>
    <ns2:SelectResponse xmlns:ns2="http://Microsoft.LobServices.OracleDB/2007/03/USER_DWDEV/Table/DW_PACK_BARCODES">
      <ns2:SelectResult>
        <ns2:DW_PACK_BARCODESRECORDSELECT>
          <ns2:PRODUCT_ID>100000005503</ns2:PRODUCT_ID>
          <ns2:SYS_BARCODE>SYS_BARCODES</ns2:SYS_BARCODE>
          <ns2:PACK_TYPE>PACK_T</ns2:PACK_TYPE>
          <ns2:CHECK_DIGIT_PACK_BARCODE>PackBarcode5503</ns2:CHECK_DIGIT_PACK_BARCODE>
          <ns2:PACK_BARCODE>PACK_BARCODEP</ns2:PACK_BARCODE>
          <ns2:CHECK_DIGIT_CARTON_BARCODE>CHECK_DIGIT_CA</ns2:CHECK_DIGIT_CARTON_BARCODE>
          <ns2:CARTON_BARCODE>CARTON_BARCOD</ns2:CARTON_BARCODE>
          <ns2:IN_DATE>1999-05-31T13:20:00.000-05:00</ns2:IN_DATE>
          <ns2:UP_DATE>1999-05-31T13:20:00.000-05:00</ns2:UP_DATE>
          <ns2:CREATEDTIME>1999-05-31T13:20:00.000-05:00</ns2:CREATEDTIME>
        </ns2:DW_PACK_BARCODESRECORDSELECT>
        <ns2:DW_PACK_BARCODESRECORDSELECT>
          <ns2:PRODUCT_ID>100000032177</ns2:PRODUCT_ID>
          <ns2:SYS_BARCODE>SYS_BARCODES</ns2:SYS_BARCODE>
          <ns2:PACK_TYPE>PACK_T</ns2:PACK_TYPE>
          <ns2:CHECK_DIGIT_PACK_BARCODE>PackBarcode32177</ns2:CHECK_DIGIT_PACK_BARCODE>
          <ns2:PACK_BARCODE>PACK_BARCODEP</ns2:PACK_BARCODE>
          <ns2:CHECK_DIGIT_CARTON_BARCODE>CHECK_DIGIT_CA</ns2:CHECK_DIGIT_CARTON_BARCODE>
          <ns2:CARTON_BARCODE>CARTON_BARCOD</ns2:CARTON_BARCODE>
          <ns2:IN_DATE>1999-05-31T13:20:00.000-05:00</ns2:IN_DATE>
          <ns2:UP_DATE>1999-05-31T13:20:00.000-05:00</ns2:UP_DATE>
          <ns2:CREATEDTIME>1999-05-31T13:20:00.000-05:00</ns2:CREATEDTIME>
        </ns2:DW_PACK_BARCODESRECORDSELECT>
      </ns2:SelectResult>
    </ns2:SelectResponse>
  </InputMessagePart_1>
</ns0:Root>

I'd like to select the value of the CHECK_DIGIT_PACK_BARCODE element that is a sibling of a given PRODUCT_ID

Following Tim's suggestion, here's my XSLT which attempts to provide a value for OuterCartonBarcode by keying from the CHECK_DIGIT_PACK_BARCODE of the ns2:SelectResponse

<?xml version="1.0" encoding="utf-16"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:var="http://schemas.microsoft.com/BizTalk/2003/var" exclude-result-prefixes="msxsl var ns2 s1" version="1.0" xmlns:ns1="http://BTS.GO.FactFeeds/DC_ArticleMaster" xmlns:s1="http://schemas.microsoft.com/BizTalk/2003/aggschema" xmlns:ns2="http://Microsoft.LobServices.OracleDB/2007/03/USER_DWDEV/Table/DW_PACK_BARCODES" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <xsl:key name="barcodes" match="ns2:DW_PACK_BARCODESRECORDSELECT" use="ns2:PRODUCT_ID" />
  <xsl:output omit-xml-declaration="yes" method="xml" version="1.0" />
  <xsl:template match="/">
    <xsl:apply-templates select="/s1:Root" />
  </xsl:template>
  <xsl:template match="/s1:Root">
    <ns1:ArrayOfArticleMasterDTO>
      <xsl:for-each select="InputMessagePart_0/ns1:ArrayOfArticleMasterDTO/ArticleMasterDTO">
        <ArticleMasterDTO>
          <AltBarcodes>
            <string>
              <xsl:value-of select="AltBarcodes/string/text()" />
            </string>
            <xsl:value-of select="AltBarcodes/text()" />
          </AltBarcodes>
          <ClientId>
            <xsl:value-of select="ClientId/text()" />
          </ClientId>
          <ArticleID>
            <xsl:value-of select="ArticleID/text()" />
          </ArticleID>
          <ArticleReference>
            <xsl:value-of select="ArticleReference/text()" />
          </ArticleReference>
          <ArticleSize>
            <xsl:value-of select="ArticleSize/text()" />
          </ArticleSize>
          <ArticleStyle>
            <xsl:value-of select="ArticleStyle/text()" />
          </ArticleStyle>
          <ArticleFit>
            <xsl:value-of select="ArticleFit/text()" />
          </ArticleFit>
          <SupplierID>
            <xsl:value-of select="SupplierID/text()" />
          </SupplierID>
          <Description>
            <xsl:value-of select="Description/text()" />
          </Description>
          <Sku>
            <xsl:value-of select="Sku/text()" />
          </Sku>
          <UnitsPerCase>
            <xsl:value-of select="UnitsPerCase/text()" />
          </UnitsPerCase>
          <UnitsPerLayer>
            <xsl:value-of select="UnitsPerLayer/text()" />
          </UnitsPerLayer>
          <xsl:variable name="var:v1" select="string(UnitsPerPallet/@xsi:nil) = 'true'" />
          <xsl:if test="string($var:v1)='true'">
            <UnitsPerPallet>
              <xsl:attribute name="xsi:nil">
                <xsl:value-of select="'true'" />
              </xsl:attribute>
            </UnitsPerPallet>
          </xsl:if>
          <xsl:if test="string($var:v1)='false'">
            <UnitsPerPallet>
              <xsl:value-of select="UnitsPerPallet/text()" />
            </UnitsPerPallet>
          </xsl:if>
          <xsl:variable name="var:v2" select="string(LayersPerPallet/@xsi:nil) = 'true'" />
          <xsl:if test="string($var:v2)='true'">
            <LayersPerPallet>
              <xsl:attribute name="xsi:nil">
                <xsl:value-of select="'true'" />
              </xsl:attribute>
            </LayersPerPallet>
          </xsl:if>
          <xsl:if test="string($var:v2)='false'">
            <LayersPerPallet>
              <xsl:value-of select="LayersPerPallet/text()" />
            </LayersPerPallet>
          </xsl:if>
          <xsl:variable name="var:v3" select="string(FullPalletWeightKG/@xsi:nil) = 'true'" />
          <xsl:if test="string($var:v3)='true'">
            <FullPalletWeightKG>
              <xsl:attribute name="xsi:nil">
                <xsl:value-of select="'true'" />
              </xsl:attribute>
            </FullPalletWeightKG>
          </xsl:if>
          <xsl:if test="string($var:v3)='false'">
            <FullPalletWeightKG>
              <xsl:value-of select="FullPalletWeightKG/text()" />
            </FullPalletWeightKG>
          </xsl:if>
          <xsl:variable name="var:v4" select="string(CaseLengthMM/@xsi:nil) = 'true'" />
          <xsl:if test="string($var:v4)='true'">
            <CaseLengthMM>
              <xsl:attribute name="xsi:nil">
                <xsl:value-of select="'true'" />
              </xsl:attribute>
            </CaseLengthMM>
          </xsl:if>
          <xsl:if test="string($var:v4)='false'">
            <CaseLengthMM>
              <xsl:value-of select="CaseLengthMM/text()" />
            </CaseLengthMM>
          </xsl:if>
          <xsl:variable name="var:v5" select="string(CaseHeightMM/@xsi:nil) = 'true'" />
          <xsl:if test="string($var:v5)='true'">
            <CaseHeightMM>
              <xsl:attribute name="xsi:nil">
                <xsl:value-of select="'true'" />
              </xsl:attribute>
            </CaseHeightMM>
          </xsl:if>
          <xsl:if test="string($var:v5)='false'">
            <CaseHeightMM>
              <xsl:value-of select="CaseHeightMM/text()" />
            </CaseHeightMM>
          </xsl:if>
          <CaseDepthMM>
            <xsl:value-of select="CaseDepthMM/text()" />
          </CaseDepthMM>
          <CaseWeightG>
            <xsl:value-of select="CaseWeightG/text()" />
          </CaseWeightG>
          <Colour>
            <xsl:value-of select="Colour/text()" />
          </Colour>
          <Level1Description>
            <xsl:value-of select="Level1Description/text()" />
          </Level1Description>
          <Level2Description>
            <xsl:value-of select="Level2Description/text()" />
          </Level2Description>
          <Level3Description>
            <xsl:value-of select="Level3Description/text()" />
          </Level3Description>
          <Level4Description>
            <xsl:value-of select="Level4Description/text()" />
          </Level4Description>
          <xsl:call-template name="PBTemplate">
            <xsl:with-param name="id" select="string(ArticleID/text())" />
          </xsl:call-template>
          <xsl:variable name="var:v6" select="string(PrimaryBarcode/@xsi:nil) = 'true'" />
          <xsl:if test="string($var:v6)='true'">
            <PrimaryBarcode>
              <xsl:attribute name="xsi:nil">
                <xsl:value-of select="'true'" />
              </xsl:attribute>
            </PrimaryBarcode>
          </xsl:if>
          <xsl:if test="string($var:v6)='false'">
            <PrimaryBarcode>
              <xsl:value-of select="PrimaryBarcode/text()" />
            </PrimaryBarcode>
          </xsl:if>
        </ArticleMasterDTO>
      </xsl:for-each>
    </ns1:ArrayOfArticleMasterDTO>
  </xsl:template>
  <xsl:template name="PBTemplate">
    <xsl:param name="id" select="ArticleID" />
    <xsl:for-each select="key('barcodes',$id)">
      <OuterCartonBarcode>
        <xsl:value-of select="/ns2:CHECK_DIGIT_PACK_BARCODE"/>
      </OuterCartonBarcode>
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>
1

There are 1 answers

2
Tim C On BEST ANSWER

Your current xpath ends with this...

/*[local-name() = 'CHECK_DIGIT_PACK_BARCODE'[PRODUCT_ID/text()="100000005503"]]

... which does not look correct.

The expression you are looking for is this....

/ns0:Root/InputMessagePart_1/*[local-name() = 'SelectResponse']/*[local-name() = 'SelectResult']/*[*[local-name()= 'PRODUCT_ID']/text()='100000005503']/*[local-name() = 'CHECK_DIGIT_PACK_BARCODE']

Or, to make it a bit more readable, this....

/ns0:Root
/InputMessagePart_1
/*[local-name() = 'SelectResponse']
/*[local-name() = 'SelectResult']
/*[*[local-name()= 'PRODUCT_ID']/text()='100000005503']
/*[local-name() = 'CHECK_DIGIT_PACK_BARCODE']

It would be much better if the expression did not try to ignore the namespaces, but used the relevant prefixes (as happens already with ns0:Root). Then the expression is much simplified

 /ns0:Root/InputMessagePart_1/ns2:SelectResponse/ns2:SelectResult/*[ns2:PRODUCT_ID='100000005503']/ns2:CHECK_DIGIT_PACK_BARCODE

Or better still, define a key like so:

<xsl:key name="barcodes" match="ns2:DW_PACK_BARCODESRECORDSELECT" use="ns2:PRODUCT_ID" />

Then you can write just this...

key('barcodes', '100000005503')/ns2:CHECK_DIGIT_PACK_BARCODE