0
votes

I'm having trouble understanding how to use axes to compare sibling nodes. I would like to select TreeNumbers that have a DescriptorRecord parent, that have a Descriptor UI child that is equal to the sibling element PharmaUI. I'm using XSLT to copy the XML but add a new element PharmaTree for every PharmaUI element.

XML

<DescriptorRecordSet>
    <DescriptorRecord>
        <DescriptorUI>apple</DescriptorUI>
        <TreeNumberList>
            <TreeNumber>A1</TreeNumber>
        </TreeNumberList>
        <Pharma>
            <PharmaUI>chocolate</PharmaUI>
            <PharmaUI>pear</PharmaUI>
        </Pharma>
    </DescriptorRecord>

    <DescriptorRecord>
        <DescriptorUI>pear</DescriptorUI>
        <TreeNumberList>
            <TreeNumber>B5</TreeNumber>
        </TreeNumberList>
    </DescriptorRecord>

    <DescriptorRecord>
        <DescriptorUI>chocolate</DescriptorUI>
        <TreeNumberList>
            <TreeNumber>C1</TreeNumber>
        </TreeNumberList>
    </DescriptorRecord>
</DescriptorRecordSet>

XSLT

    <xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xsd ="http://www.w3.org/2001/XMLSchema#">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<xsl:template match="/">    
    <xsl:for-each select="DescriptorRecordSet/DescriptorRecord">
        <DescriptorRecord>
            <DescriptorUI><xsl:value-of select = "DescriptorUI/text()"/></DescriptorUI>
            <Pharma>
                <xsl:for-each select="Pharma/PharmaUI">
                    <PharmaUI><xsl:value-of select = "text()"/></PharmaUI>
                    <PharmaTree>
                        <xsl:value-of select = "//TreeNumber[preceding-sibling::DescriptorUI[text() = ../Pharma/PharmaUI/text()]]/text()"/>
                    </PharmaTree>
                </xsl:for-each>
            </Pharma>
        </DescriptorRecord>
    </xsl:for-each>
</xsl:template>

</xsl:stylesheet>

Currently the XPath used is not working:

//TreeNumber[preceding-sibling::DescriptorUI[text() = ../Pharma/PharmaUI/text()]]/text()

I expect the output to look like this for the first record (as it's the only one with PharmaUI elements):

    <DescriptorRecord>
        <DescriptorUI>apple</DescriptorUI>
        <TreeNumberList>
            <TreeNumber>A1</TreeNumber>
        </TreeNumberList>
        <Pharma>
            <PharmaUI>chocolate</PharmaUI>
            <PharmaTree>C1</PharmaTree>
            <PharmaUI>pear</PharmaUI>
            <PharmaTree>B5</PharmaTree>
        </Pharma>
    </DescriptorRecord>

I'd like for someone to explain why this XPath does not work and how I can fix it to select the correct element, thank you.

1

1 Answers

0
votes

Cross-references in XML documents are best handled in XSLT using keys:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0">

  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>

  <xsl:key name="tree-ref" match="DescriptorRecord/TreeNumberList/TreeNumber" use="../../DescriptorUI"/>

  <xsl:template match="@* | node()">
      <xsl:copy>
          <xsl:apply-templates select="@* | node()"/>
      </xsl:copy>
  </xsl:template>

  <xsl:template match="PharmaUI">
      <xsl:copy-of select="."/>
      <ParmaTree>
          <xsl:value-of select="key('tree-ref', .)"/>
      </ParmaTree>
  </xsl:template>

</xsl:stylesheet>

https://xsltfiddle.liberty-development.net/gVhDDyG