rappot
rappot

Reputation: 89

Creating attribute nodes from HTML style attribute value by XSL

I'm trying to change a XML document into xsl:fo by using xslt. In some element nodes of the XML there is HTML style attribute like attribute node, and I want to change its value into separated attribute nodes

sample of XML is...

<books>
    <book>
        <price style="mycolor:red;myvalign:center;">10</price>
    </book>
</books>

and I want to change it like this

<books>
    <book>
        <price mycolor="red" myvalign="center">10</price>
    </book>
</books>

I can tokenize the value of 'style' attribute node (got help from stackoverflow here) and rendering it as key-value paired but I don't know how can I set these key-value pair as attribute of node of calling template.

Below is a xslt I tried, but has error. Could you help me please?

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="xs"
    version="1.0">

    <xsl:template match="books">
        <xsl:element name="books">
            <xsl:apply-templates />
        </xsl:element>

    </xsl:template>

    <xsl:template match="book">
        <xsl:element name="book">
            <xsl:apply-templates />
        </xsl:element>
    </xsl:template>

    <xsl:template match="price">
        <xsl:variable name="style" select="./@style" />

        <xsl:variable name="result">
            <xsl:call-template name="tokenize">
                <xsl:with-param name="style" select="$style" />
            </xsl:call-template>
        </xsl:variable>

        <xsl:element name="price">
            <xsl:value-of select="$result"/>
            <xsl:value-of select="."/>  
        </xsl:element>
    </xsl:template>

    <xsl:template name="tokenize">
        <xsl:param name="style" />
        <xsl:variable name="styleFrag" select="substring-before($style, ';')" />
        <xsl:if test="$styleFrag">
            <xsl:variable name="styleKey" select="substring-before($styleFrag, ':')" />
            <xsl:variable name="styleVal" select="substring-after($styleFrag, ':')" />
            <xsl:variable name="concat1">&lt;xsl:attribute name=&apos;</xsl:variable>
            <xsl:variable name="concat2">&apos;&gt;&lt;xsl:value-of select=&apos;</xsl:variable>
            <xsl:variable name="concat3">&apos; /&gt;&lt;/xsl:attribute&gt;</xsl:variable>
            <xsl:variable name="concatted" select="concat($concat1, $styleKey, $concat2, $styleVal, $concat3)" />
            <xsl:value-of select="$concatted" />
            <xsl:call-template name="tokenize"> 
                <xsl:with-param name="style" select="substring-after($style,';')" /> 
            </xsl:call-template>
        </xsl:if>  
    </xsl:template>

</xsl:stylesheet>

Thank you.

Upvotes: 0

Views: 902

Answers (2)

michael.hor257k
michael.hor257k

Reputation: 117073

Couldn't this be simpler?

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

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

<xsl:template match="@style">
    <xsl:call-template name="tokenize-style">
        <xsl:with-param name="style" select="."/>
    </xsl:call-template>
</xsl:template>

<xsl:template name="tokenize-style">
    <xsl:param name="style"/>
        <xsl:if test="contains($style, ';')">
        <xsl:variable name="declaration" select="substring-before($style, ';')" />
        <xsl:attribute name="{normalize-space(substring-before($declaration, ':'))}">
            <xsl:value-of select="substring-after($declaration, ':')"/>
        </xsl:attribute>
            <!-- recursive call -->
            <xsl:call-template name="tokenize-style">
                <xsl:with-param name="style" select="substring-after($style, ';')" />
            </xsl:call-template>
        </xsl:if>
</xsl:template>

</xsl:stylesheet>

Upvotes: 1

Martin Honnen
Martin Honnen

Reputation: 167696

Change

<xsl:template match="price">
    <xsl:variable name="style" select="./@style" />

    <xsl:variable name="result">
        <xsl:call-template name="tokenize">
            <xsl:with-param name="style" select="$style" />
        </xsl:call-template>
    </xsl:variable>

    <xsl:element name="price">
        <xsl:value-of select="$result"/>
        <xsl:value-of select="."/>  
    </xsl:element>
</xsl:template>

<xsl:template name="tokenize">
    <xsl:param name="style" />
    <xsl:variable name="styleFrag" select="substring-before($style, ';')" />
    <xsl:if test="$styleFrag">
        <xsl:variable name="styleKey" select="substring-before($styleFrag, ':')" />
        <xsl:variable name="styleVal" select="substring-after($styleFrag, ':')" />
        <xsl:variable name="concat1">&lt;xsl:attribute name=&apos;</xsl:variable>
        <xsl:variable name="concat2">&apos;&gt;&lt;xsl:value-of select=&apos;</xsl:variable>
        <xsl:variable name="concat3">&apos; /&gt;&lt;/xsl:attribute&gt;</xsl:variable>
        <xsl:variable name="concatted" select="concat($concat1, $styleKey, $concat2, $styleVal, $concat3)" />
        <xsl:value-of select="$concatted" />
        <xsl:call-template name="tokenize"> 
            <xsl:with-param name="style" select="substring-after($style,';')" /> 
        </xsl:call-template>
    </xsl:if>  
</xsl:template>

to

<xsl:template match="price">
    <xsl:variable name="style" select="./@style" />

    <price>
        <xsl:call-template name="tokenize">
            <xsl:with-param name="style" select="$style" />
        </xsl:call-template>

        <xsl:value-of select="."/>  
    </price>
</xsl:template>

<xsl:template name="tokenize">
    <xsl:param name="style" />
    <xsl:variable name="styleFrag" select="substring-before($style, ';')" />
    <xsl:if test="$styleFrag">
        <xsl:variable name="styleKey" select="substring-before($styleFrag, ':')" />
        <xsl:variable name="styleVal" select="substring-after($styleFrag, ':')" />
        <xsl:attribute name="{normalize-space($styleKey)}">
          <xsl:value-of select="$styleValue"/>
        </xsl:attribute>


        <xsl:call-template name="tokenize"> 
            <xsl:with-param name="style" select="substring-after($style,';')" /> 
        </xsl:call-template>
    </xsl:if>  
</xsl:template>

Untested, but should show the approach.

Upvotes: 1

Related Questions