How can I sort elements by new counted values in their child element. XSLT 1.0
I have an xml sheet of this kind:
<houses>
<house number="1">
<mainroom>
<roomprice>5</roomprice>
<roomtax>2</roomtax>
</mainroom>
<roompricefull>
<price value="8"/>
</roompricefull>
</house>
<house number="2">
<mainroom>
<roomprice>3</roomprice>
<roomtax>1</roomtax>
</mainroom>
<roompricefull>
<price value="7"/>
</roompricefull>
</house>
<house number="3">
<mainroom>
<roomprice>9</roomprice>
<roomtax>1</roomtax>
</mainroom>
<roompricefull>
<price value="4"/>
</roompricefull>
</house>
<house number="4">
<mainroom>
<roomprice>12</roomprice>
<roomtax>3</roomtax>
</mainroom>
<roompricefull>
<price value="6"/>
</roompricefull>
</house>
</houses>
so I had to change the value of attribute "value" in "price" element in each of the "house" with the sum of "roomprice" value and "roomtax"
I wrote an xsl transformation of such a kind:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://开发者_运维知识库www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template name="PriceChange" match="price[parent::roompricefull]">
<xsl:copy>
<xsl:variable name="sn" select="../../@number"/>
<xsl:variable name="TaxValue" select="number(//house[@number=string($sn)]/mainroom/roomtax)"/>
<xsl:variable name="BaseValue" select="number(//house[@number=string($sn)]/mainroom/roomprice)"/>
<xsl:attribute name="value">
<xsl:value-of select="string($TaxValue+$BaseValue)"/>
</xsl:attribute>
<!--xsl:for-each select="/houses/house">
<xsl:sort select="houses/house[$sn]/roompricefull/@value"/>
</xsl:for-each-->
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
But when I started working on sorting "house" elements by my new value, I found problems. I actually don't understand why it's not working, so I commented my last of the dozens examples in up code.
I got this:
<houses>
<house number="1">
<mainroom>
<roomprice>5</roomprice>
<roomtax>2</roomtax>
</mainroom>
<roompricefull>
<price value="7"/>
</roompricefull>
</house>
<house number="2">
<mainroom>
<roomprice>3</roomprice>
<roomtax>1</roomtax>
</mainroom>
<roompricefull>
<price value="4"/>
</roompricefull>
</house>
<house number="3">
<mainroom>
<roomprice>9</roomprice>
<roomtax>1</roomtax>
</mainroom>
<roompricefull>
<price value="10"/>
</roompricefull>
</house>
<house number="4">
<mainroom>
<roomprice>12</roomprice>
<roomtax>3</roomtax>
</mainroom>
<roompricefull>
<price value="15"/>
</roompricefull>
</house>
</houses>
But the expected result was:
<houses>
<house number="4">
<mainroom>
<roomprice>12</roomprice>
<roomtax>3</roomtax>
</mainroom>
<roompricefull>
<price value="15"/>
</roompricefull>
</house>
<house number="3">
<mainroom>
<roomprice>9</roomprice>
<roomtax>1</roomtax>
</mainroom>
<roompricefull>
<price value="10"/>
</roompricefull>
</house>
<house number="1">
<mainroom>
<roomprice>5</roomprice>
<roomtax>2</roomtax>
</mainroom>
<roompricefull>
<price value="7"/>
</roompricefull>
</house>
<house number="2">
<mainroom>
<roomprice>3</roomprice>
<roomtax>1</roomtax>
</mainroom>
<roompricefull>
<price value="4"/>
</roompricefull>
</house>
</houses>
It would be great if you could help me with sorting and explaining why my example is not working. Seems that I don't understand the meaning of <sort/>, but everything I find tells me just about the usage of it without any explanation. Thank you in advance.
it would be great if you could help me with sorting
This is the correct XSLT 1.0 approach.
Notice the correct use of xsl:sort
which needs:
- the data-type to be specified, being string the default while we need here number
- the best use of sort inside the
xsl:apply-templates
- the application of sorting with the sorting keys of the input document combined as required (sum).
the sorting order also to be specified being the default
ascending
<xsl:output indent="yes"/> <xsl:strip-space elements="*"/> <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> <xsl:template match="houses"> <xsl:copy> <xsl:apply-templates select="house"> <xsl:sort select="mainroom/roomprice + mainroom/roomtax" data-type="number" order="descending"/> </xsl:apply-templates> </xsl:copy> </xsl:template> <xsl:template match="price/@value"> <xsl:value-of select=" ../../../mainroom/roomprice + ../../../mainroom/roomtax"/> </xsl:template>
explaining why my example is not working
Your transform does no work mainly because you are trying to sort elements in the wrong context (inside a template matching a deep child in the tree). Moreover:
- you are trying to sort indicating a sorting key by absolute XPath pattern.
- you are not specifying the needed
xsl:sort
attributes - you would like final elements be sorted according to a value calculated afterward and not present in the input document. This is not the correct approach. You must always use values present in the input document, eventually combining them properly.
Sort by the new price:
<xsl:template match="/">
<xsl:for-each select="house">
<xsl:sort select="mainroom/roomprice + mainroom/roomtax"/>
<xsl:apply-templates select="."/>
</xsl:for-each>
</xsl:template>
As before:
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
Adjust the price in the output:
<xsl:template match="roompricefull">
<roompricefull>
<price value="{../mainroom/roomprice + ../mainroom/roomtax}"/>
</roompricefull>
</xsl:template>
精彩评论