Aaron
Aaron

Reputation: 271

Build XML structure from attributes in single node

I have the following XML source:

<element not-relevant="true" bold="true" superscript="true" subscript="false" text="stuff"/>

In any order, I need to loop through specific attributes (i.e. only ones which are related to the HTML I'm building: bold/superscript/subscript, etc.) and where one of these attributes evaluates to 'true', output nested elements to get the following output:

<strong>
    <sup>
        stuff
    </sup>
</strong>

I have a feeling I need to use some sort of recursion like follows (without the infinite loop, of course):

<xsl:template match="element">
    <xsl:call-template name="content">
        <xsl:with-param name="text" select="@text"/>
    </xsl:call-template>
</xsl:template>

<xsl:template name="content">
    <xsl:param name="text"/>
    <xsl:choose>
        <xsl:when test="@bold = 'true'">
            <strong>
                <xsl:copy>
                    <xsl:call-template name="content">
                        <xsl:with-param name="text" select="$text"/>
                    </xsl:call-template>
                <xsl:copy>
            </strong>
        </xsl:when>
        <xsl:when test="@subscript = 'true'">
            <sub>
                <xsl:copy>
                    <xsl:call-template name="content">
                        <xsl:with-param name="text" select="$text"/>
                    </xsl:call-template>
                <xsl:copy>
            </sub>
        </xsl:when>
        <xsl:when test="@superscript = 'true'">
            <sup>
                <xsl:copy>
                    <xsl:call-template name="content">
                        <xsl:with-param name="text" select="$text"/>
                    </xsl:call-template>
                <xsl:copy>
            </sup>
        </xsl:when>
        <xsl:otherwise>
            <xsl:value-of select="$text" disable-output-escaping="yes"/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

Any ideas? I'm looking for the cleanest XSLT 2.0 solution and appreciate the help.

Thanks,

Upvotes: 5

Views: 107

Answers (1)

Ian Roberts
Ian Roberts

Reputation: 122364

This is a nice use case for <xsl:next-match/>:

<xsl:template match="element" priority="1">
  <xsl:value-of select="@text" />
</xsl:template>

<xsl:template match="element[@superscript = 'true']" priority="2">
  <sup><xsl:next-match/></sup>
</xsl:template>

<xsl:template match="element[@subscript = 'true']" priority="3">
  <sub><xsl:next-match/></sub>
</xsl:template>

<xsl:template match="element[@bold = 'true']" priority="4">
  <strong><xsl:next-match/></strong>
</xsl:template>

When you apply templates to an element element it'll first fire the highest priority matching template, and if that template uses next-match it will delegate to the next highest priority, etc. Your example element in the question matches the first, second and fourth templates above, so initially the "bold" template would be selected, then that would delegate to the "superscript" one and finally to the generic element one, resulting in <strong><sup>stuff</sup></strong>.

As you can see from this example, the priority numbers are what determines the order of nesting - if the priorities of the second and fourth templates were reversed you'd get <sup><strong>stuff</strong></sup> instead.

Upvotes: 6

Related Questions