Reputation: 109
Sorry for my English. I need sort nodes of this XML data
<root>
<section id="1">
<news-item id="1" pub-date="2012-01-03" />
<news-item id="2" pub-date="2012-01-04" />
<news-item id="3" pub-date="2011-12-21" />
</section>
<section id="2">
<news-item id="4" pub-date="2012-01-05" />
<news-item id="5" pub-date="2012-01-06" />
<news-item id="6" pub-date="2012-01-07" />
</section>
<section id="3">
<news-item id="7" pub-date="2012-02-10" />
<news-item id="8" pub-date="2012-02-11" />
<news-item id="9" pub-date="2012-02-12" />
</section>
</root>
to this
<root>
<section id="3">
<news-item id="9" pub-date="2012-02-12" />
<news-item id="8" pub-date="2012-02-11" />
<news-item id="7" pub-date="2012-02-10" />
</section>
<section id="2">
<news-item id="6" pub-date="2012-01-07" />
<news-item id="5" pub-date="2012-01-06" />
<news-item id="4" pub-date="2012-01-05" />
</section>
<section id="1">
<news-item id="2" pub-date="2012-01-04" />
<news-item id="1" pub-date="2012-01-03" />
<news-item id="3" pub-date="2011-12-21" />
</section>
</root>
i.e. I need first sort news-item elements by pub-date in section, and then sort section element by max pub-date in news-item. (Section with lastes news must be on top).
Many thanks!
Upvotes: 1
Views: 301
Reputation: 243449
An alternative, somewhat simpler, 2-pass solution (no xsl:variable
, no generate-id()
, no pipe-separated values, but using msxsl:node-set()
):
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
exclude-result-prefixes="msxsl">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="vrtfPass1">
<xsl:apply-templates mode="pass1"/>
</xsl:variable>
<xsl:template match="/*">
<root>
<xsl:for-each select=
"msxsl:node-set($vrtfPass1)/*/*">
<xsl:sort select="*[1]/@pub-date" order="descending"/>
<xsl:copy-of select="."/>
</xsl:for-each>
</root>
</xsl:template>
<xsl:template match="node()|@*" mode="pass1">
<xsl:copy>
<xsl:apply-templates select="node()|@*" mode="pass1"/>
</xsl:copy>
</xsl:template>
<xsl:template match="section" mode="pass1">
<section id="{@id}">
<xsl:apply-templates select="*" mode="pass1">
<xsl:sort select="@pub-date" order="descending"/>
</xsl:apply-templates>
</section>
</xsl:template>
</xsl:stylesheet>
when applied on the following XML document (based on the provided, but made more "interesting):
<root>
<section id="1">
<news-item id="1" pub-date="2012-01-03" />
<news-item id="2" pub-date="2012-01-04" />
<news-item id="3" pub-date="2011-12-21" />
</section>
<section id="2">
<news-item id="4" pub-date="2012-01-05" />
<news-item id="5" pub-date="2012-01-06" />
<news-item id="6" pub-date="2012-01-07" />
<news-item id="7" pub-date="2222-12-22" />
</section>
<section id="3">
<news-item id="7" pub-date="2012-02-10" />
<news-item id="8" pub-date="2012-02-11" />
<news-item id="9" pub-date="2012-02-12" />
</section>
</root>
the wanted, correct result is produced:
<root>
<root>
<section id="2">
<news-item id="7" pub-date="2222-12-22"/>
<news-item id="6" pub-date="2012-01-07"/>
<news-item id="5" pub-date="2012-01-06"/>
<news-item id="4" pub-date="2012-01-05"/>
</section>
<section id="3">
<news-item id="9" pub-date="2012-02-12"/>
<news-item id="8" pub-date="2012-02-11"/>
<news-item id="7" pub-date="2012-02-10"/>
</section>
<section id="1">
<news-item id="2" pub-date="2012-01-04"/>
<news-item id="1" pub-date="2012-01-03"/>
<news-item id="3" pub-date="2011-12-21"/>
</section>
</root>
</root>
Upvotes: 0
Reputation: 60190
This is your pastebin with the names fixed and a different sort for the sections:
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="section">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:apply-templates select="news-item">
<xsl:sort select="@pub-date" data-type="text" order="descending" />
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="root">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:variable name="sectionOrder">
<xsl:text>|</xsl:text>
<xsl:for-each select="section/news-item">
<xsl:sort select="@pub-date" data-type="text" order="descending" />
<xsl:value-of select="generate-id(..)"/>
<xsl:text>|</xsl:text>
</xsl:for-each>
</xsl:variable>
<xsl:apply-templates select="section">
<xsl:sort select="string-length(substring-before($sectionOrder, concat('|',generate-id(),'|')))" data-type="number" />
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
The names in your pastebin don't match (newsitem
vs. news-item
, pubdate
vs. pub-date
), so I fixed that. The output I get seems to be right (also with the test case you added in your comment):
<root>
<section id="2">
<news-item id="7" pub-date="2222-12-22" />
<news-item id="6" pub-date="2012-01-07" />
<news-item id="5" pub-date="2012-01-06" />
<news-item id="4" pub-date="2012-01-05" />
</section>
<section id="3">
<news-item id="10" pub-date="2012-02-12" />
<news-item id="9" pub-date="2012-02-11" />
<news-item id="8" pub-date="2012-02-10" />
</section>
<section id="1">
<news-item id="2" pub-date="2012-01-04" />
<news-item id="1" pub-date="2012-01-03" />
<news-item id="3" pub-date="2011-12-21" />
</section>
</root>
Upvotes: 2