0
votes

person,

I have an XML with a structure like this:

<root>
  <concepts>
    <concept id="1" version="1">
      <name>This is the name of 1.1</name>
    </concept>
    <concept id="1" version="2">
      <name>This is the name of 1.2</name>
    </concept>
    <concept id="2" version="1">
      <name>This is the name of 2.1</name>
    </concept>
  </concepts>
  <structures>
    <structure id="1">
      <conceptRef id="2" version="1" />
    </structure>
    <structure id="2">
      <conceptRef id="1" version="2" />
    </structure>
  </structures>
</root>

I want to get the text within the name child-node of concept based on attribute values in structure/conceptRef child node. The output for the example above should be along those lines:

  • Structure 1: This is the name of 2.1
  • Structure 2: This is the name of 1.2

So I currently have something like this:

<xsl:template match="structures">
  <xsl:for-each select=".//structure">
    Structure <xsl:value-of select="@id" />: <!-- TODO: what goes here -->
  </xsl:for-each>
</xsl:template>

What I do not know is, how I can nest the XPath query to find the nodes from the other tree based on current context. For debugging purposes, I have now added three different lines to test the approach:

    <xsl:template match="structures">
        <xsl:for-each select=".//structure">
            Structure <xsl:value-of select="@id" />: 
             0: <xsl:value-of select="./conceptRef/@id" />.<xsl:value-of select="./conceptRef/@version" />
             a: <xsl:value-of select="//concepts/concept[@id=./conceptRef/@id and @version=./conceptRef/@version]/name" />
             b: <xsl:value-of select="//concepts/concept[@id=1 and @version=2]/name" />
        </xsl:for-each>
    </xsl:template>

The output is:

            Structure 1: 
             0: 2.1
             a: 
             b: This is the name of 1.2
            Structure 2: 
             0: 1.2
             a: 
             b: This is the name of 1.2

It means that what is under 0 delivers the right values for the filter. In line 2 I see the hardcoded value that works as well. Just when I combine the two in line a, the result is for some reason empty.

Any ideas?

Thanks, Daniel.

2

2 Answers

1
votes

I strongly recommend using a key to resolve cross-references. The following stylesheet:

XSLT 1.0

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

<xsl:key name="concept" match="concept" use="concat(@id, '|', @version)" />

<xsl:template match="/root">
    <xsl:for-each select="structures/structure">
        <xsl:text>Structure </xsl:text>
        <xsl:value-of select="@id" />
        <xsl:text>: </xsl:text>
        <xsl:value-of select="key('concept', concat(conceptRef/@id, '|', conceptRef/@version))/name" />
        <xsl:text>&#10;</xsl:text>
    </xsl:for-each>
</xsl:template>

</xsl:stylesheet>

applied to your input example, will produce:

Result

Structure 1: This is the name of 2.1
Structure 2: This is the name of 1.2
0
votes

You want to use the current() function e.g. //concepts/concept[@id=current()/conceptRef/@id and @version=current()/conceptRef/@version]/name for your paths to work. For efficiency, you might want to replace that lookup by declaring a key and using the key function.