Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Build XML structure from attributes in single node

Tags:

xml

xslt

xslt-2.0

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,

like image 710
Aaron Avatar asked Sep 23 '14 16:09

Aaron


1 Answers

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.

like image 105
Ian Roberts Avatar answered Nov 13 '22 15:11

Ian Roberts