开发者

xslt sort output xml

I'm trying to find a solution to the following problem.

I'm 开发者_JAVA技巧developing XSLT transformation (which is now about 40KB big) that is transforming quite complex XMLs into a quite simple structure which would like this:

<Records>
<Record key="XX">
</Record> 
<Record key="XX1">
</Record>
<Record key="XX2">
</Record>
<Record key="XX3">
</Record>
</Records>

I would like to have this output XML sorted according to Records/Record/@key values. The problem is that my XSLT produces this output unsorted and due to its complexity I am unable to sort it there. Is it possible to apply xsl:sort on the output XML? I know that I can prepare another XSLT transform, but in my case that's not the solution, as I'm limited to only one XSLT.. Please, help!...


Is it possible to apply xsl:sort on the output XML?

Yes, multipass processing is possible, and especially in XSLT 2.0 you don't even need to apply an xxx:node-set() extension on the result, because the infamous RTF type does no longer exist:

<xsl:stylesheet version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="/">
  <xsl:variable name="vPass1">
   <!--
        Put/Invoke your cirrent code here   
        to generate the following           
-->
    <Records>
      <Record key="XX3">
      </Record>
      <Record key="XX2">
      </Record>
      <Record key="XX4">
      </Record>
      <Record key="XX1">
      </Record>
    </Records>
  </xsl:variable>

  <xsl:apply-templates select="$vPass1/*"/>
 </xsl:template>

 <xsl:template match="Records">
  <Records>
   <xsl:perform-sort select="*">
    <xsl:sort select="@key"/>
   </xsl:perform-sort>
  </Records>
 </xsl:template>
</xsl:stylesheet>

When this transformation is performed on any XML document (not used/ignored), the wanted, correct, sorted result is produced:

<Records>
   <Record key="XX1"/>
   <Record key="XX2"/>
   <Record key="XX3"/>
   <Record key="XX4"/>
</Records>

In XSLT 1.0 it is almost the same with the additional conversion of the result from RTF type to a normal tree:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:ext="http://exslt.org/common"
 exclude-result-prefixes="ext">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="/">
  <xsl:variable name="vrtfPass1">
   <!--
        Put/Invoke your cirrent code here   
        to generate the following           
-->
    <Records>
      <Record key="XX3">
      </Record>
      <Record key="XX2">
      </Record>
      <Record key="XX4">
      </Record>
      <Record key="XX1">
      </Record>
    </Records>
  </xsl:variable>

  <xsl:variable name="vPass1"
                select="ext:node-set($vrtfPass1)"/>

  <xsl:apply-templates select="$vPass1/*"/>
 </xsl:template>

 <xsl:template match="Records">
  <Records>
   <xsl:for-each select="*">
    <xsl:sort select="@key"/>

    <xsl:copy-of select="."/>
   </xsl:for-each>
  </Records>
 </xsl:template>
</xsl:stylesheet>


40Kb is a lot of code for one stylesheet. When things get to this kind of scale, it's usually best to split a transformation into a pipeline of smaller transformations. If you have such a pipeline architecture, then adding a sort step at the end is trivial. There are plenty of technologies for managing a pipeline of transformations (XProc, Orbeon, xmlsh, ant, Coccoon) depending on your requirements. The benefit of pipelining is that it keeps your code modular and reusable.


As an addendum to Dimitre's excellent solution above, if you're using an XSLT 1.0 processor (for example, .NET), the following can give you a pointer as to how to use node-set: http://www.xml.com/pub/a/2003/07/16/nodeset.html#tab.namespaces

In my case, I was in .NET 1.1 (i.e. MSXML) and the solution looked something like:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:msxsl="urn:schemas-microsoft-com:xslt">
    <xsl:template match="/">            
        <xsl:variable name="vrtfPass1">
            <Records xmlns="">  
                    <xsl:apply-templates />
            </Records >            
        </xsl:variable>
        <xsl:variable name="vPass1" select="msxsl:node-set($vrtfPass1)"/>
        <xsl:apply-templates select="$vPass1/*" mode="sorting"/>
    </xsl:template>
    <xsl:template match="Records" mode="sorting">
        <Records>
       <xsl:for-each select="Record">
        <xsl:sort select="@key"/>
        <xsl:copy-of select="."/>
       </xsl:for-each>
      </Records>
    </xsl:template>
</xsl:stylesheet>
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜