Sort parent nodes XML depends on child nodes values
I have a following XML
<Root>
<Element A/>
<Element B/>
<Data1>
<DataElement/>
<Values>
<Value>2222</Value>
<Name>field1</Name>
</Values>
<Values>
<Value>ABC</Value>
<Name>field2</Name>
</Values>
</Data1>
<Data2>
<DataElement/>
<Values>
<Value>1111</Value>
<Name>field1</Name>
</Values>
<Values>
<Value>XYZ</Value>
<Name>field2</Name>
</Values>
</Data2>
<DataN>
...
</DataN>
</Root>
I need to get the same XML with Data section sorted by "Value" of specified field name, for exmple: sort by "field1" will return
<Root>
<Element A/>开发者_Python百科;
<Element B/>
<Data2>
<DataElement/>
<Values>
<Value>1111</Value>
<Name>field1</Name>
</Values>
<Values>
<Value>XYZ</Value>
<Name>field2</Name>
</Values>
</Data2>
<Data1>
<DataElement/>
<Values>
<Value>2222</Value>
<Name>field1</Name>
</Values>
<Values>
<Value>ABC</Value>
<Name>field2</Name>
</Values>
</Data1>
<DataN>
...
</DataN>
</Root>
Also, I have to send name of sort field as parameter...
This transformation implements exactly the stated requirements. It takes special care to preserve the exact order of the elements that are not to be sorted. No other answer at present does this:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="vField" select="'field1'"/>
<xsl:param name="pSortType" select="'number'"/>
<xsl:template match="node()|@*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[starts-with(name(),'Data')]">
<xsl:variable name="vPos" select=
"count(preceding-sibling::*[starts-with(name(),'Data')])+1"/>
<xsl:for-each select="/*/*[starts-with(name(),'Data')]">
<xsl:sort select="Values[Name=$vField]/Value"
data-type="{$pSortType}"/>
<xsl:if test="position() = $vPos">
<xsl:copy-of select="."/>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
When applied on the following XML document (the same as the provided one, but with an additional <Element C=""/>
inserted between Data1
and Data2
so that we can verify the preservation of ordering of the non-sorted elements):
<Root>
<Element A=""/>
<Element B=""/>
<Data1>
<DataElement/>
<Values>
<Value>2222</Value>
<Name>field1</Name>
</Values>
<Values>
<Value>ABC</Value>
<Name>field2</Name>
</Values>
</Data1>
<Element C=""/>
<Data2>
<DataElement/>
<Values>
<Value>1111</Value>
<Name>field1</Name>
</Values>
<Values>
<Value>XYZ</Value>
<Name>field2</Name>
</Values>
</Data2>
</Root>
produces the wanted, correct result -- note that the position of <Element C=""/>
is preserved:
<Root>
<Element A=""/>
<Element B=""/>
<Data2>
<DataElement/>
<Values>
<Value>1111</Value>
<Name>field1</Name>
</Values>
<Values>
<Value>XYZ</Value>
<Name>field2</Name>
</Values>
</Data2>
<Element C=""/>
<Data1>
<DataElement/>
<Values>
<Value>2222</Value>
<Name>field1</Name>
</Values>
<Values>
<Value>ABC</Value>
<Name>field2</Name>
</Values>
</Data1>
</Root>
I think you can go with this simple transform:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="field" select="'field1'"/>
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Root">
<xsl:copy>
<xsl:apply-templates select="@*|*">
<xsl:sort select="Values[Name=$field]/
Value[string(number(.))!='NaN']"
data-type="number"/>
<xsl:sort select="Values[Name=$field]/
Value[string(number(.))='NaN']"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
When applied to your input (corrected to make it well-formed):
<Root>
<Element A=""/>
<Element B=""/>
<Data1>
<DataElement/>
<Values>
<Value>2222</Value>
<Name>field1</Name>
</Values>
<Values>
<Value>ABC</Value>
<Name>field2</Name>
</Values>
</Data1>
<Data2>
<DataElement/>
<Values>
<Value>1111</Value>
<Name>field1</Name>
</Values>
<Values>
<Value>XYZ</Value>
<Name>field2</Name>
</Values>
</Data2>
</Root>
produces:
<Root>
<Element A=""></Element>
<Element B=""></Element>
<Data2>
<DataElement></DataElement>
<Values>
<Value>1111</Value>
<Name>field1</Name>
</Values>
<Values>
<Value>XYZ</Value>
<Name>field2</Name>
</Values>
</Data2>
<Data1>
<DataElement></DataElement>
<Values>
<Value>2222</Value>
<Name>field1</Name>
</Values>
<Values>
<Value>ABC</Value>
<Name>field2</Name>
</Values>
</Data1>
</Root>
Explanation:
- Use of identity rule to copy every thing as is.
- override the required DataN elements and simple application of sort condition based on $field input parameter.
- data-type for sorting dynamically changes according to element field type
精彩评论