0

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 Answers 2

1

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
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks a lot! I did not know the key function. I made many things too complicated up to now :)
0

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.