5

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,

1 Answer 1

6

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.

Sign up to request clarification or add additional context in comments.

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.