xsl基于节点值与不同父级的兄弟姐妹

时间:2014-09-02 07:25:37

标签: xslt xpath xslt-2.0

类似于这个链接xsl sum siblings based on node value,我需要应用一个xsl转换,该转换根据其兄弟节点之一的值对某些节点值求和,问题是有类似的节点我想得到总和因为不同的父母,我无法找出xslt。这是xml的样子。

<?xml version="1.0" encoding="ISO-8859-1" ?>
<Message>
<Body>
    <Order>
        <Item1>
            <.../>
            <Type>widget</Type>
            <Qty>20</Qty>
            <.../>
        </Item1>
        <Item>
            <.../>
            <Type>gadget</Type>
            <Qty>10</Qty>
            <.../>
        </Item>
        <Item2>
            <.../>
            <Type>widget</Type>
            <Qty>5</Qty>
            <.../>
        </Item2>
        <Item3>
            <.../>
            <Type>widget</Type>
            <Qty>2</Qty>
            <.../>
        </Item3>
        <Item3>
            <.../>
            <Type>Other</Type>
            <Qty>0</Qty>
            <.../>
        </Item3>
        <Item/>
    </Order>
   </Body>
</Message>

我希望能够通过类型小部件进行分组并得到数量的总和,我的xslt是这样的。

<xsl:value-of select="sum(/Message/Body/Order/Item1|Item2|Item3[Type = 'widget'] /Qty)"/>

我的代码没有给我一个错误,但它也没有返回任何内容。我希望我的输出如下所示,而无需对xml上的类型进行硬编码,例如widget,gadget,因为它也可以更改为其他类型。

小部件总数= 27
小工具总数= 10

我还想从输出中排除包含和等于0的Type。

谢谢! RAF

3 个答案:

答案 0 :(得分:0)

试着改为:

<xsl:value-of select="sum(/Message/Body/Order//Qty[preceding-sibling::Type[.='widget']])"/>

<xsl:value-of select="sum(/Message/Body/Order//Qty[preceding-sibling::Type[.='gadget']])"/>

分别产生2710

修改

在xslt-2.0中,您可以使用xsl:for-each-group,如下所示

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="2.0">

    <xsl:strip-space elements="*"/>

    <xsl:output indent="yes" omit-xml-declaration="yes"/>

    <xsl:template match="/">
        <xsl:for-each-group select="/Message/Body/Order/*" group-by="Type">
            <xsl:value-of select="concat(current-grouping-key(), ': ')"/>
            <xsl:copy-of select="sum(current-group()/Qty)"/>
            <xsl:text>&#xA;</xsl:text>
        </xsl:for-each-group>
    </xsl:template>


</xsl:stylesheet>

输出:

widget: 27
gadget: 10
Other: 0

答案 1 :(得分:0)

您可以考虑在此处使用key来查找项目。假设您想要求和的项目始终是order元素的子项,它们的键将如下所示:

 <xsl:key name="item" match="Order/*" use="Type" />

然后总结一下&#34;小部件&#34;项目,你会这样做:

<xsl:value-of select="sum(key('item', 'widget')/Qty)" />

如果你想从输出中排除总数为0的总和,只需使用变量,然后检查

<xsl:variable name="widget" select="sum(key('item', 'widget')/Qty)" />
<xsl:if test="$widget > 0">
    <xsl:text>Widget Total = </xsl:text>
    <xsl:value-of select="$widget" />
</xsl:if>

如果你想避免硬编码,一种方法是使用xsl:for-each-group来获取不同的项目,例如:

  <xsl:for-each-group select="//Order/*" group-by="Type">
    <xsl:variable name="value" select="sum(key('item', Type)/Qty)" />
    <xsl:if test="$value > 0">
        <xsl:value-of select="Type" />
        <xsl:text> Total = </xsl:text>
        <xsl:value-of select="$value" />
        <xsl:value-of select="'&#10;'" />
    </xsl:if>
  </xsl:for-each-group>

答案 2 :(得分:0)

当前表达式的问题是运算符优先级规则 - 您的XPath

/Message/Body/Order/Item1|Item2|Item3[Type = 'widget']/Qty

解析为

(/Message/Body/Order/Item1)
|
(Item2)
|
(Item3[Type = 'widget']/Qty)

由于您已将问题标记为XSLT-2.0,因此您可以使用括号:

/Message/Body/Order/(Item1|Item2|Item3)[Type = 'widget']/Qty

如果您事先并不知道所有可能的名字,请使用通配符

/Message/Body/Order/*[Type = 'widget']/Qty

至于更一般的问题,这看起来不错。 for-each-group

<xsl:for-each-group select="/Message/Body/Order/*" group-by="Type">
  <xsl:variable name="totalQty" select="sum(current-group()/Qty)"/>
  <xsl:if test="$totalQty gt 0">
    <item type="{current-grouping-key()}" qty="{$totalQty}"/>
  </xsl:if>
</xsl:for-each-group>