开发者

How can I sort elements by new counted values in their child element. XSLT 1.0

开发者 https://www.devze.com 2023-03-13 14:26 出处:网络
I have an xml sheet of this kind: <houses> <house number=\"1\"> <mainroom> <roomprice>5</roomprice>

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>
0

精彩评论

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

关注公众号