Ralph B
Ralph B

Reputation: 15

Accessing attributes from dynamically created sibling elements

Learning XSLT!

I have an XML source tree fragment that looks like this:

Using Saxon and XSLT 2.0 to transform...

   <?xml version = "1.0" encoding = "UTF-8"?>
    <Chapter revision="B" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
        <Content Ref="X123">
            <Ref name="INT1"/>
            <Ref name="INT2"/>
            <Ref name="INT3"/>
            <Ref name="INT4"/>
            <Ref name="INT5"/>
        </Content>
        <Data>
            <Reference name="INT1" Function="CONDUCTOR"></Reference>
            <Reference name="INT2" Function="SIGNAL"></Reference>
            <Reference name="INT3" Function="MIXED"></Reference>
            <Reference name="INT4" Function="PLANE"></Reference>
            <Reference name="INT5" Function="CORE"></Reference>
        </Data>
    </Chapter>

I want it to produce this:

<Chapter>
   <Content>
      <Ref id="INT1" Function="CONDUCTOR">INT1</Ref>
      <Ref id="INT2" Function="SIGNAL">INT2</Ref>
      <Ref id="INT3" Function="MIXED">INT3</Ref>
      <Ref id="INT4" Function="PLANE">INT4</Ref>
      <Ref id="INT5" Function="CORE">INT5</Ref>
   </Content>
</Chapter>

Here's my template fragment:

<xsl:template match="/">
    <xsl:apply-templates select="/Chapter"/>
</xsl:template>

<xsl:template match="/Chapter">
    <xsl:element name="{name()}">
        <xsl:apply-templates select="Content"/>
    </xsl:element>
</xsl:template>

<xsl:template match="Content">
    <xsl:element name="{name()}">
        <xsl:apply-templates select="Ref"/>
    </xsl:element>
</xsl:template>

<xsl:template match="Ref">
    <xsl:element name="{name()}">

        <xsl:attribute name="id">
            <xsl:value-of select="@name"/>
        </xsl:attribute>

        <xsl:attribute name="Function">
            <xsl:value-of select="/Chapter/Data/Reference[@name=/Chapter/Data/Reference/@name]/@Function"/>
        </xsl:attribute>

        <xsl:value-of select="@name"/>

    </xsl:element>
</xsl:template>

But the template above produces this: It is obviously picking up the values from every node

 <Chapter>
       <Content>
          <Ref id="INT1" Function="CONDUCTOR SIGNAL MIXED PLANE CORE">INT1</Ref>
          <Ref id="INT2" Function="CONDUCTOR SIGNAL MIXED PLANE CORE">INT2</Ref>
          <Ref id="INT3" Function="CONDUCTOR SIGNAL MIXED PLANE CORE">INT3</Ref>
          <Ref id="INT4" Function="CONDUCTOR SIGNAL MIXED PLANE CORE">INT4</Ref>
          <Ref id="INT5" Function="CONDUCTOR SIGNAL MIXED PLANE CORE">INT5</Ref>
       </Content>
    </Chapter>

What do I need to supply as a predicate value to step through the attribute values?

Many thanks

Upvotes: 1

Views: 95

Answers (1)

Martin Honnen
Martin Honnen

Reputation: 167716

Change /Chapter/Data/Reference[@name=/Chapter/Data/Reference/@name]/@Function to /Chapter/Data/Reference[@name=current()/@name]/@Function, then learn about keys and define <xsl:key name="ref" match="Data/Reference" use="@name"/> and use key('ref', @name)/@Function instead to improve performance and readability.

In general you might want to learn about literal result elements and attribute value templates so that

<xsl:template match="Ref">
    <xsl:element name="{name()}">

        <xsl:attribute name="id">
            <xsl:value-of select="@name"/>
        </xsl:attribute>

        <xsl:attribute name="Function">
            <xsl:value-of select="/Chapter/Data/Reference[@name=/Chapter/Data/Reference/@name]/@Function"/>
        </xsl:attribute>

        <xsl:value-of select="@name"/>

    </xsl:element>
</xsl:template>

becomes

<xsl:template match="Ref">
    <Ref id="{@name}" Function="{key('ref', @name)/@Function}">
        <xsl:value-of select="@name"/>
    </Ref>
</xsl:template>

and the complete code is

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="xs"
    version="2.0">

    <xsl:key name="ref" match="Data/Reference" use="@name"/>

    <xsl:template match="/Chapter">
        <xsl:copy copy-namespaces="no">
            <xsl:apply-templates select="Content"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="Content">
        <xsl:copy copy-namespaces="no">
            <xsl:apply-templates/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="Ref">
        <Ref id="{@name}" Function="{key('ref', @name)/@Function}">
            <xsl:value-of select="@name"/>
        </Ref>
    </xsl:template>

</xsl:stylesheet>

Upvotes: 0

Related Questions