views:

243

answers:

3

Hello everyone,

I was wondering if there is a way of displaying element's attributes in a specific order, with a use of sequence or somehow establishing an order rather than writing it explicitly.

Sorry, explanation is a bit unclear. The example might help:

So I have a template:

<xsl:template match="Element/@1|@2|@3|@4">
    <xsl:if test="string(.)">
        <span>
            <xsl:value-of select="."/><br/>
        </span>
    </xsl:if>
</xsl:template>

And I want attributes to appear in the order 1, 2, 3, 4. Unfortunately, you can't garantee the order of attributes in XML, it could be <Element 2="2" 4="4" 3="3" 1="1">

So the template above will produce the following:

<span>2</span>
<span>4</span>
<span>3</span>
<span>1</span>

Ideally I don't want to test each attribute if it has got a value. I was wondering if I can somehow set an order of my display? Or will I need to do it explicitly and repeating the if test as in:

<xsl:template match="Element">

    <xsl:if test="string(./@1)>
        <span>
            <xsl:value-of select="./@1"/><br/>
        </span>
    </xsl:if>
    ...
    <xsl:if test="string(./@4)>
        <span>
            <xsl:value-of select="./@4"/><br/>
        </span>
    </xsl:if>
</xsl:template>

Would be interesting to know what can be done in this case.

Many thanks!

+1  A: 

I'd use xsl:sort on the local-name of the attribute to get the result you want. I'd also use a different mode so the results don't get called by accident somewhere else.

<xsl:stylesheet version="1.1" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;

  <xsl:template match="Element">
      <xsl:apply-templates select="@*" mode="sorted">
        <xsl:sort select="local-name()" />
    </xsl:apply-templates>
  </xsl:template>

  <xsl:template match="Element/@a|@b|@c|@d" mode="sorted">
    <xsl:if test="string(.)">
        <span>
            <xsl:value-of select="."/><br/>
        </span>
    </xsl:if>
  </xsl:template>
</xsl:stylesheet>
Mark Worth
+3  A: 

Do not produce XML that relies on the order of the attributes. This is very brittle and I would consider it bad style, to say the least. XML was not designed in that way; <elem a="1" b="2" /> and <elem a="1" b="2" /> are explicitly equivalent.

If you want ordered output, order your output (instead of relying on ordered input).

Furthermore, match="Element/@1|@2|@3|@4" is not equivalent to match="Element/@1|Element/@2|Element/@3|Element/@4", but I'm sure you mean the latter.

That being said, you can do:

<xsl:template match="Element/@1|Element/@2|Element/@3|Element/@4">
  <xsl:if test="string(.)">
    <span>
      <xsl:value-of select="."/><br/>
    </span>
  </xsl:if>
</xsl:template>

<xsl:template match="Element">
  <xsl:apply-templates select="@1|@2|@3|@4">
    <!-- order your output... -->
    <xsl:sort select="name()" />
  </xsl:apply-templates>
</xsl:template>

EDIT: I'll take it as read that @1 etc are just examples, because names cannot actually start with a number in XML.

Tomalak
@Tomalak, hi, there is a spelling mistake .. second template must match "Element", (fixed ..)
infant programmer
@infant programmer: Thanks for spotting/fixing the typo!
Tomalak
+3  A: 

In an earlier question you seemed to use XSLT 2.0 so I hope this time too an XSLT 2.0 solution is possible. The order is not determined in the match pattern of a template, rather it is determined when you do xsl:apply-templates. So (with XSLT 2.0) you can simply write a sequence of the attributes in the order you want e.g. <xsl:apply-templates select="@att2, @att1, @att3"/> will process the attributes in that order. With XSLT 1.0 you don't have sequences but only node-sets so there, if you want to have the same result, you need multiple xsl:apply-templates in the order you want e.g. <xsl:apply-templates select="@att2"/><xsl:apply-templates select="@att1"/><xsl:apply-templates select="@att3"/>.

Martin Honnen
+1, pragmatic approach.
Tomalak
I ended up using this approach. Thanks!
DashaLuna