Sorting XML recursively - only the inner nodes are sorted
I need to sort an example XML in a way that all the ShippingPoints are sorted first, then the Cargos according their first ShippingPoint and finally the Transports according to the first ShippingPoint in their first Cargo. So basically I am attempting to sort all the Transports according to the date when they are supposed to start.
Now, I found a solution using XSL recursion, except that only the Cargos and ShippingPoints are sorted as expected - the most outer Transport nodes are not. I am wondering what I am doing wrong here. Both MSXML (VS2008) and Saxon parsers are giving me exactly same result.
Example XML code:
<?xml version="1.0" encoding="utf-8"?>
<Transports>
<Transport ID="1893">
<Cargos>
<Cargo ID="1532" >
<ShippingPoints>
<ShippingPoint ID="1600" ArrivesOn="2011-04-07T12:00:00" />
<ShippingPoint ID="1601" ArrivesOn="2011-04-08T12:00:00" />
</ShippingPoints>
</Cargo>
<Cargo ID="1532">
<ShippingPoints>
<ShippingPoint ID="1601" ArrivesOn="2011-03-08T12:00:00" />
<ShippingPoint ID="1600" ArrivesOn="2011-02-07T12:00:00" />
</ShippingPoints>
</Cargo>
</Cargos>
</Transport>
<Transport ID="1891" >
<Cargos>
<Cargo ID="1529" >
<Shippin开发者_JAVA百科gPoints>
<ShippingPoint ID="1594" ArrivesOn="2011-04-14T12:00:00" />
<ShippingPoint ID="1595" ArrivesOn="2011-04-04T13:00:00" />
</ShippingPoints>
</Cargo>
<Cargo ID="1530" >
<ShippingPoints>
<ShippingPoint ID="1597" ArrivesOn="2011-04-09T18:00:00" />
<ShippingPoint ID="1596" ArrivesOn="2011-04-04T12:00:00" />
</ShippingPoints>
</Cargo>
</Cargos>
</Transport>
<Transport ID="1892">
<Description/>
<Cargos>
<Cargo ID="1531" >
<ShippingPoints>
<ShippingPoint ID="1599" ArrivesOn="2011-04-06T18:00:00" />
<ShippingPoint ID="1598" ArrivesOn="2011-04-05T12:00:00" />
</ShippingPoints>
</Cargo>
<Cargo ID="1531" >
<ShippingPoints>
<ShippingPoint ID="1599" ArrivesOn="2011-04-02T18:00:00" />
<ShippingPoint ID="1598" ArrivesOn="2011-04-03T12:00:00" />
</ShippingPoints>
</Cargo>
</Cargos>
</Transport>
</Transports>
XSLT code:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output method="xml" version="1.0" encoding="UTF-8" />
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="ShippingPoints">
<xsl:copy>
<xsl:apply-templates select="ShippingPoint">
<xsl:sort select="@ArrivesOn" />
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="Cargos">
<xsl:copy>
<xsl:apply-templates select="Cargo">
<xsl:sort select="ShippingPoints/ShippingPoint[1]/@ArrivesOn" />
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="Transports">
<xsl:copy>
<xsl:apply-templates select="Transport">
<xsl:sort select="Cargos/Cargo[1]/ShippingPoints/ShippingPoint[1]/@ArrivesOn"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Unless I didn't understand this, you are looking for a sort by minimum @ArrivesOn
descendant. The shortest stylesheet:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:apply-templates select="node()">
<xsl:sort select="min(.//@ArrivesOn/xs:dateTime(.))"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Output:
<Transports>
<Transport ID="1893">
<Cargos>
<Cargo ID="1532">
<ShippingPoints>
<ShippingPoint ID="1600" ArrivesOn="2011-02-07T12:00:00"/>
<ShippingPoint ID="1601" ArrivesOn="2011-03-08T12:00:00"/>
</ShippingPoints>
</Cargo>
<Cargo ID="1532">
<ShippingPoints>
<ShippingPoint ID="1600" ArrivesOn="2011-04-07T12:00:00"/>
<ShippingPoint ID="1601" ArrivesOn="2011-04-08T12:00:00"/>
</ShippingPoints>
</Cargo>
</Cargos>
</Transport>
<Transport ID="1892">
<Description/>
<Cargos>
<Cargo ID="1531">
<ShippingPoints>
<ShippingPoint ID="1599" ArrivesOn="2011-04-02T18:00:00"/>
<ShippingPoint ID="1598" ArrivesOn="2011-04-03T12:00:00"/>
</ShippingPoints>
</Cargo>
<Cargo ID="1531">
<ShippingPoints>
<ShippingPoint ID="1598" ArrivesOn="2011-04-05T12:00:00"/>
<ShippingPoint ID="1599" ArrivesOn="2011-04-06T18:00:00"/>
</ShippingPoints>
</Cargo>
</Cargos>
</Transport>
<Transport ID="1891">
<Cargos>
<Cargo ID="1530">
<ShippingPoints>
<ShippingPoint ID="1596" ArrivesOn="2011-04-04T12:00:00"/>
<ShippingPoint ID="1597" ArrivesOn="2011-04-09T18:00:00"/>
</ShippingPoints>
</Cargo>
<Cargo ID="1529">
<ShippingPoints>
<ShippingPoint ID="1595" ArrivesOn="2011-04-04T13:00:00"/>
<ShippingPoint ID="1594" ArrivesOn="2011-04-14T12:00:00"/>
</ShippingPoints>
</Cargo>
</Cargos>
</Transport>
</Transports>
If you want a step by step transformation then you should use modes and variables to store temporary results, as in the following XSLT 2.0 example:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" indent="yes" encoding="UTF-8" />
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:variable name="t1">
<xsl:apply-templates mode="step1"/>
</xsl:variable>
<xsl:variable name="t2">
<xsl:apply-templates select="$t1/node()" mode="step2"/>
</xsl:variable>
<xsl:apply-templates select="$t2/node()"/>
</xsl:template>
<xsl:template match="@*|node()" mode="#all">
<xsl:copy>
<xsl:apply-templates select="@*, node()" mode="#current"/>
</xsl:copy>
</xsl:template>
<xsl:template match="ShippingPoints" mode="step1">
<xsl:copy>
<xsl:apply-templates select="ShippingPoint">
<xsl:sort select="@ArrivesOn" />
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="Cargos" mode="step2">
<xsl:copy>
<xsl:apply-templates select="Cargo">
<xsl:sort select="ShippingPoints/ShippingPoint[1]/@ArrivesOn" />
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="Transports">
<xsl:copy>
<xsl:apply-templates select="Transport">
<xsl:sort select="Cargos/Cargo[1]/ShippingPoints/ShippingPoint[1]/@ArrivesOn"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
I think that does what you want with an XSLT 2.0 processor.
精彩评论