XSLT Gotcha When Combining Attribute Sets

Social Share Toolbar

I made my first foray into XSLT territory recently. Quick definition from W3Schools:

“XSL stands for EXtensible Stylesheet Language, and is a style sheet language for XML documents. XSLT stands for XSL Transformations.”

Overall the experience was a good one. It was very easy to figure out how to display an XML document in an HTML report format. It was also very easy to style the report using attribute sets. Essentially, attribute sets allow you to define styles for different elements in your document. Elements use “use-attribute-sets” to define the sets that should be applied. For example:

<xsl:attribute-set name="bodystyle">
    <xsl:attribute name="bgcolor">
        #cccccc
    </xsl:attribute>
</xsl:attribute-set>

<xsl:template match="report">
    <html>
        <body xsl:use-attribute-sets="bodystyle">
            <xsl:apply-templates select="headerData"/>
            <xsl:apply-templates select="reportData"/>
        </body>
    </html>
</xsl:template>

The code above would make the body background color #cccccc. Works great. But what if I wanted to set a property that isn’t an attribute of the element. What if I want to set the font color, for example. There is no “color” attribute for body. In this case, the style attribute can be used:

<xsl:attribute-set name="defaultfontcolor">
    <xsl:attribute name="style">
        color:#111;
    </xsl:attribute>
</xsl:attribute-set>

<xsl:template match="report">
    <html>
        <body xsl:use-attribute-sets="defaultfontcolor">
            <xsl:apply-templates select="headerData"/>
            <xsl:apply-templates select="reportData"/>
        </body>
    </html>
</xsl:template>

So far, so good. The cool thing about attribute sets is that they can be combined, too. Let’s say I want to combine the two examples above and set the body color and font color at the same time. I could define both in the same attribute set, but I want to use the font color elsewhere without the bgcolor. This can be done easily:


<xsl:attribute-set name="bodystyle">
    <xsl:attribute name="bgcolor">
        #cccccc
    </xsl:attribute>
</xsl:attribute-set>

<xsl:attribute-set name="defaultfontcolor">
    <xsl:attribute name="style">
        color:#111;
    </xsl:attribute>
</xsl:attribute-set>

<xsl:template match="report">
    <html>
        <body xsl:use-attribute-sets="bodystyle defaultfontcolor">
            <xsl:apply-templates select="headerData"/>
            <xsl:apply-templates select="reportData"/>
        </body>
    </html>
</xsl:template>

As you can see in the body tag I’ve listed both attribute sets, separated by a space. This works great!

Now here’s the gotcha. The last attribute set in the list will override previous style definitions of the same attribute. Normally, this isn’t a problem…as long as you’re not using the style attribute in more than one set. While the style attribute can define more than one element property, “style” itself is the attribute. Therefore, if you try to combine two attribute sets that both define “style”, only the last one will be implemented, regardless of whether they define different properties.

For example, although the code below defines the same styles as the one above, only the defaultfontcolor attribute set will be implemented. This is because both sets define the same attribute: “style”.


<xsl:attribute-set name="bodystyle">
    <xsl:attribute name="style">
        background: #ccc;
    </xsl:attribute>
</xsl:attribute-set>

<xsl:attribute-set name="defaultfontcolor">
    <xsl:attribute name="style">
        color:#111;
    </xsl:attribute>
</xsl:attribute-set>

<xsl:template match="report">
    <html>
        <body xsl:use-attribute-sets="bodystyle defaultfontcolor">
            <xsl:apply-templates select="headerData"/>
            <xsl:apply-templates select="reportData"/>
        </body>
    </html>
</xsl:template>

It doesn’t matter that one defines background and the other defines color. It only matters that they both set the “style” attribute. And because defaultfontcolor is last in the use-attribute-sets list, its definition is used over bodystyle.