Question

I am trying to make an XSLT transformation from XML, I want to transform font style tags into HTML tags, but my I am doing something wrong. My XML file is like this one :

<root>
    <p>
        <span>
            <i/>
                italic
        </span>
        <span>
            <i/>
            <b/>
                bold-italic
        </span>
        <span>
                normal
        </span>
    </p>
</root>

What I want is HTML with the same tags but my XSLT transformation does not work: HTML:

<p>
    <i>italic</i> 
    <i><b>bold-italic</b></i> 
    normal
<p>

I was trying xsl:if condition but it does not work,i do not know what I am doing wrong: XSLT:

<xsl:template match="p">
    <p>
         <xsl:for-each select="span">
             <xsl:if test="i">
                    <i>
                    <xsl:value-of select="."/> 
                    </i>  
                </xsl:if>
                <xsl:if test="b">
                    <b>
                    <xsl:value-of select="."/> 
                    </b>  
                </xsl:if>
            </xsl:for-each> 
    </p>
</xsl:template>

Do you know how to repair my code ?

Was it helpful?

Solution

Can you have more than just b and i elements? It may be possible to do this with a generic solution, that creates a nested element for each child element of a span element.

This solution uses a recursive template, that matches span, but with a parameter contain the index number of the child element that needs to be output. When this index exceeds the number of child elements, the text is output.

Try this XSLT too:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" indent="yes"/>

    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="span">
        <xsl:param name="num" select="1"/>
        <xsl:variable name="childElement" select="*[$num]"/>
        <xsl:choose>
            <xsl:when test="$childElement">
                <xsl:element name="{local-name($childElement)}">
                    <xsl:apply-templates select=".">
                        <xsl:with-param name="num" select="$num + 1"/>
                    </xsl:apply-templates>
                </xsl:element>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="."/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet>

This does assume that all the span element only contain elements you want to nest, in addition to the text.

OTHER TIPS

You can test the contents of span element using an XPath expression with a predicate which tests for its contents, and match different templates for each situation. Since you need b and i for bold-italic, you should use that expression in one of your predicates.

The stylesheet below does the transformation using only templates (without the need of a for-each). I'm assuming the contents of your <span> elements is text (not mixed content):

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:strip-space elements="*"/>

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

    <xsl:template match="span[i]">
        <i><xsl:value-of select="."/></i>
    </xsl:template>

    <xsl:template match="span[b]">
        <b><xsl:value-of select="."/></b>
    </xsl:template>

    <xsl:template match="span[i and b]">
        <i><b><xsl:value-of select="."/></b></i>
    </xsl:template>

</xsl:stylesheet>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top