Xml XSLT将排序与求和结合起来

Xml XSLT将排序与求和结合起来,xml,sorting,xslt,sum,Xml,Sorting,Xslt,Sum,假设我有以下XML: <A name="a1"> <x t="1">4.0</x> <A name="a1.1"> <x t="1">2.0</x> <x t="2">3.0</x> </A> <A name="a1.2"> <x t="1">4.0</x> <

假设我有以下XML:

<A name="a1">
    <x t="1">4.0</x>
    <A name="a1.1">
        <x t="1">2.0</x>
        <x t="2">3.0</x>
    </A>
    <A name="a1.2">
         <x t="1">4.0</x>
    </A>
    <A name="a1.3">
         <x t="1">4.0</x>
         <x t="2">2.0</x>
         <x t="3">5.0</x>
    </A>
</A>

4
2
3
4
4
2
5
我使用的是Saxon 6.5.5和XSLT1.0

这里我有一个递归结构,但为了简单起见,让我们假设嵌套最多只允许两个级别,即顶部a元素可能不包含任何元素,一个或多个其他a元素,但这些子a元素不能包含子子a元素等等。每个sub-A元素至少包含onx元素,其中包含一些浮点数值。顶级A元素有一个x元素,它根据子A元素的数量存储每个子A元素中所有最大x值的平均值。在上面的示例中,我们有3个子元素:a1.1的最大值为3,a1.2的最大值为4,a1.3的最大值为5。顶层A元素a1相应地具有(3+4+5)/3=4的值。还请注意,如果子元素中的多个值相同且最高(例如:2,3,4,4->两个元素的值为4,这也是最大值),则只考虑一个。最后,这些值的唯一用途是为它们所表示的子元素指定一个最大值

<A name="a1">
    <x t="1">4.0</x>--------<---------<---------| (sum(3,4,5) div count(A))=12/3=4
    <A name="a1.1">                             |
        <x t="1">2.0</x>-----|__max(2,3)=3----->+
        <x t="2">3.0</x>-----|                  |
    </A>                                        |
    <A name="a1.2">                             |
         <x t="1">4.0</x>-----|__max(4)=4------>+
    </A>                                        |
    <A name="a1.3">                             |
         <x t="1">4.0</x>-----|                 |
         <x t="2">2.0</x>-----|--max(4,2,5)=5-->+
         <x t="3">5.0</x>-----|
    </A>
</A>

4.0--------+
|
|
4.0-----|                 |
2.0-------max(4,2,5)=5-->+
5.0-----|
我的问题是提取每个sub-A元素中的最高值,并将所有这些值与XSLT函数sum()相结合

我知道

<xsl:for-each select="x">
    <xsl:sort select="." data-type="number" order="ascending"/>
    <xsl:if test="position() = last()">
        <xsl:value-of select="."/>
    </xsl:if>
</xsl:for-each>

将对我的x进行排序,并在这里取下,因为我使用升序将给出最大的数值

另一方面

    <xsl:value-of select="sum(A/x) div count(A)"/>

将返回每个子A元素中所有x元素的总和。在这里,我没有为每个顶级元素循环包含外部for each循环,因为它使事情变得更复杂,所以现在让我们说,我只关注上面的示例。如果每个子A元素包含单个x元素,这将按预期工作。一旦我在某个子元素中得到多个x元素,它就会变得一团糟


我上面描述的事情应该是可行的,如果XSLT是过程性语言而不是函数式语言,那么它实际上是小菜一碟。但是,我对函数式编程(包括XSLT)没有任何经验。:/如果我们在这里讨论过程代码,可以简单地声明一组变量(一个用于存储当前和,一个用于存储当前子a元素中的最大数,一个用于存储子a元素的数量)在循环遍历每个子元素时,从每个x元素中提取最大值,并将其添加到迄今为止的总和中。最后剩下的就是除以子A元素的数量,并将值存储在顶级A元素的x元素中。

您能将XSLT 2.0与类似Saxon 9的XSLT 2.0处理器一起使用吗?然后您可以简单地编写
sum(A/max(x))
来计算
A
元素子元素的最大
x
值之和(然后当然
sum(A/max(x))div count(A)
来计算平均值。使用XSLT1.0,您需要
sum(A/x[不是(
分别用于平均
和(A/x[not(.
。表达式是XPath表达式,在XSLT属性中,如果使用XSLT 1.0,则需要转义

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:math="http://exslt.org/math"
extension-element-prefixes="math">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

<!-- identity transform -->
<xsl:template match="@*|node()">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="/A">
    <xsl:copy>
        <xsl:apply-templates select="@*"/>
        <avg-of-maxima>
            <xsl:value-of select="sum(A/x[.=math:max(../x)][1]) div count(A)"/>
        </avg-of-maxima>
    <xsl:apply-templates select="node()"/>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

应用于测试输入

<A name="a1">
   <A name="a1.1">
      <x t="1">2.0</x>
      <x t="2">3.0</x>
   </A>
   <A name="a1.2">
      <x t="1">4.0</x>
   </A>
   <A name="a1.3">
      <x t="1">4.0</x>
      <x t="2">2.0</x>
      <x t="3">5.0</x>
      <x t="4">5.0</x>
   </A>
</A>

2
3
4
4
2
5
5
结果是:

<?xml version="1.0" encoding="UTF-8"?>
<A name="a1">
   <avg-of-maxima>4</avg-of-maxima>
   <A name="a1.1">
      <x t="1">2.0</x>
      <x t="2">3.0</x>
   </A>
   <A name="a1.2">
      <x t="1">4.0</x>
   </A>
   <A name="a1.3">
      <x t="1">4.0</x>
      <x t="2">2.0</x>
      <x t="3">5.0</x>
      <x t="4">5.0</x>
   </A>
</A>

4.
2
3
4
4
2
5
5

如果最大值有两个或多个
x
同级,该怎么办?只考虑一个同级。这就是为什么排序并取最后一个()或第一个()(取决于您对值的排序方式)非常合适。我会将此添加到问题中。感谢您的提示。请指出XSLT 1.0或2.0。如果使用XSLT 1.0,了解哪个特定处理器将非常有用。--附:我不确定通过限制嵌套来简化是一个好主意;答案可能不适合您的实际情况。谢谢,在开始时添加了信息g、 至于递归结构——它实际上有两个子层次。我还没有弄清楚是否可以限制递归,所以我决定添加这个只有两个层次的最小示例,因为它很好地代表了我的情况。嗨,我在问题的开头添加了信息。很抱歉,遗漏了这一点。至于你的建议ion它似乎不起作用,或者可能是我做错了。:D我仍然得到了所有等级的总和,而不是每个sub-a元素的最大值。您能详细说明一下您的解决方案吗?好的,我决定切换到XSLT 2.0,因为在我的情况下使用它要容易得多(不仅在本例中,而且在其他一些情况下)不要用v1.0编写急促的XPath表达式。您的XSLT 2.0解决方案工作得很好!非常感谢。^ ^威尔
<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:math="http://exslt.org/math"
extension-element-prefixes="math">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

<!-- identity transform -->
<xsl:template match="@*|node()">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="/A">
    <xsl:copy>
        <xsl:apply-templates select="@*"/>
        <avg-of-maxima>
            <xsl:value-of select="sum(A/x[.=math:max(../x)][1]) div count(A)"/>
        </avg-of-maxima>
    <xsl:apply-templates select="node()"/>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>
<A name="a1">
   <A name="a1.1">
      <x t="1">2.0</x>
      <x t="2">3.0</x>
   </A>
   <A name="a1.2">
      <x t="1">4.0</x>
   </A>
   <A name="a1.3">
      <x t="1">4.0</x>
      <x t="2">2.0</x>
      <x t="3">5.0</x>
      <x t="4">5.0</x>
   </A>
</A>
<?xml version="1.0" encoding="UTF-8"?>
<A name="a1">
   <avg-of-maxima>4</avg-of-maxima>
   <A name="a1.1">
      <x t="1">2.0</x>
      <x t="2">3.0</x>
   </A>
   <A name="a1.2">
      <x t="1">4.0</x>
   </A>
   <A name="a1.3">
      <x t="1">4.0</x>
      <x t="2">2.0</x>
      <x t="3">5.0</x>
      <x t="4">5.0</x>
   </A>
</A>