Peter Webster
Peter Webster

Reputation: 103

Differential formatting for elements according to attribute values

I'm putting together a simple XSL stylesheet so colleagues can preview XML that they are editing in their browser. One element has many different attribute values, each of which needs to be rendered differently.

   <hi rend="b">

needs to be bold,

   <hi rend="b i"> 

needs to be bold and italic, and so on.

What do I need to do in the XSL to make this happen?

I've done a good deal of Googling and haven't found a solution yet; perhaps it's a very basic question, but any help very gratefully received.

Upvotes: 0

Views: 39

Answers (2)

Michael Kay
Michael Kay

Reputation: 163322

The solution from @zx485 requires 4 templates if there are 2 styles, 8 if there are 3, 16 if there are 4: that's not very scaleable.

For comparison here's an XSLT 3.0 solution (which you could run in Saxon-JS) that will handle a completely open-ended set of styles:

<xsl:function name="f:render" as="element()">
  <xsl:param name="e" as="element()"/>
  <xsl:param name="styles" as="xs:string*"/>
  <xsl:choose>
    <xsl:when test="empty($styles)">
      <xsl:copy select="$e">
        <xsl:copy-of select="@* except @rend"/>
        <xsl:apply-templates/>
      </xsl:copy>
    </xsl:when>
    <xsl:otherwise>
      <xsl:element name="{head($styles)}">
        <xsl:sequence select="f:render($e, tail($styles))"/>
      </xsl:element>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

then

<xsl:template match="*[@rend]">
  <xsl:sequence select="f:render(., tokenize(@rend))"/>
</xsl:template>

Upvotes: 1

zx485
zx485

Reputation: 29022

Because you wrote your colleagues preview XML in their browser I assume that you expect an XSLT-1.0 solution. The following templates copy the hi element and replaces the attribute with b and i tags. The copied hi tags are ignored by the browser.

However, in this solution you have to create combinations of each attribute value.

<xsl:template match="hi[contains(@rend,'i')]">
    <xsl:copy>
      <i><xsl:apply-templates /></i>
    </xsl:copy>
</xsl:template>

<xsl:template match="hi[contains(@rend,'b')]">
    <xsl:copy>
      <b><xsl:apply-templates /></b>
    </xsl:copy>
</xsl:template>

<xsl:template match="hi[contains(@rend,'i') and contains(@rend,'b')]">
    <xsl:copy>
      <i><b><xsl:apply-templates /></b></i>
    </xsl:copy>
</xsl:template>

Output:

<hi><i><b> 
  ...3...
</b></i></hi>      

<hi><i> 
  ...1...
</i></hi>

<hi><b> 
  ...2...
</b></hi>

Upvotes: 2

Related Questions