1

请帮我解决 XSLT 中的数学问题。我需要对具有相同 unit_id 的 price*VAT 字段求和。

输入 XML:

 <Root> 
<RowSet>
            <unit_id>1<unit_id>
            <price>100<unit_id>
            <VAT>2<VAT> 
</RowSet> 
<RowSet>
            <unit_id>1<unit_id>
            <price>200<unit_id>
            <VAT>3<VAT> 
</RowSet> 
<RowSet>
            <unit_id>2<unit_id>
            <price>300<unit_id>
            <VAT>4<VAT> 
</RowSet>
 </Root>

预期的输出必须是这样的:

<Root>
<Output>
        <unit_id>1<unit_id>
        <total>800<total> <?-(100*2+200*3)-?>
</Output>
<Output>
        <unit_id>2<unit_id>
        <total>1200<total>  <?-(300*4)-?>
</Output>
</Root>

我在每个周期中都尝试过 sum(price*VAT) ,但没有帮助。

我试着用这个:

<Root>
<xsl:variable name="ID">
<xsl:value-of select="unit_id"/>
</xsl:variable>                   
<xsl:for-each select="/Root/RowSet[unit_id = $ID]">
<Output>
<xsl:if test="position() = count (/Root/RowSet[unit_id = $ID])">
<total>
<xsl:value-of select="sum(/Root/RowSet[unit_id = $ID]/price * /Root/RowSet[unit_id = $ID]/VAT)"/>
</total>
</xsl:if>
</Output>
</Root>

错误的输出是:

   <Root>
    <Output>
            <unit_id>1<unit_id>
            <total>200<total> <?-(100*2)-?>
    </Output>
    <Output>
            <unit_id>2<unit_id>
            <total>1200<total>  <?-(300*4)-?>
    </Output>
    </Root>
4

2 回答 2

0

这是一个非递归的两遍解决方案

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:ext="http://exslt.org/common" exclude-result-prefixes="ext">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:key name="kRSByUId" match="RowSet" use="unit_id"/>

 <xsl:variable name="vrtfPass1"><xsl:apply-templates/></xsl:variable>

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

 <xsl:template match="/">
  <xsl:apply-templates mode="pass2" select="ext:node-set($vrtfPass1)/*"/> 
 </xsl:template>

 <xsl:template mode="pass2" match="/*">
  <Root>
   <xsl:apply-templates mode="pass2" select=
    "RowSet[generate-id()
           =
            generate-id(key('kRSByUId', unit_id)[1])]"/>
  </Root>
 </xsl:template>

 <xsl:template mode="pass2" match="RowSet">
  <RowSet>
   <xsl:copy-of select="unit_id"/>
   <total>
     <xsl:value-of select="sum(key('kRSByUId', unit_id)/subTotal)"/>
   </total>
  </RowSet>
 </xsl:template>

 <xsl:template match="RowSet/price">
  <subTotal><xsl:value-of select=". * ../VAT"/></subTotal>
 </xsl:template>

 <xsl:template match="VAT"/>
</xsl:stylesheet>

当此转换应用于提供的 XML 文档时(已纠正严重的格式错误):

<Root>
    <RowSet>
        <unit_id>1</unit_id>
        <price>100</price>
        <VAT>2</VAT>
    </RowSet>
    <RowSet>
        <unit_id>1</unit_id>
        <price>200</price>
        <VAT>3</VAT>
    </RowSet>
    <RowSet>
        <unit_id>2</unit_id>
        <price>300</price>
        <VAT>4</VAT>
    </RowSet>
</Root>

产生了想要的正确结果

<Root>
   <RowSet>
      <unit_id>1</unit_id>
      <total>800</total>
   </RowSet>
   <RowSet>
      <unit_id>2</unit_id>
      <total>1200</total>
   </RowSet>
</Root>

说明

  1. 在第一遍中,我们将源 XML 文档转换为:

<Root>
   <RowSet>
      <unit_id>1</unit_id>
      <subTotal>200</subTotal>
   </RowSet>
   <RowSet>
      <unit_id>1</unit_id>
      <subTotal>600</subTotal>
   </RowSet>
   <RowSet>
      <unit_id>2</unit_id>
      <subTotal>1200</subTotal>
   </RowSet>
</Root>

.2. 在第二遍中,我们执行经典的 Muenchian 分组,并在每个组中汇总subTotal子节点并生成一个total元素,其文本节点子节点是计算得出的总和。

于 2012-11-07T13:20:34.503 回答
0

运行总计是 XSLT 中的一个常见问题。递归是解决这个问题的方法。(这不是 XSLT 独有的——任何没有可变变量的语言都是这样工作的。)

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:template match="/">
    <Root>
      <xsl:apply-templates select="Root/RowSet" />
    </Root>
  </xsl:template>

  <xsl:template match="RowSet">
    <!-- only do work for the *first* RowSet with any particular ID -->
    <xsl:if test="not(preceding-sibling::RowSet/unit_id = current()/unit_id)">
      <Output>
        <xsl:copy-of select="unit_id" />
        <total>
          <xsl:call-template name="running-total">
            <xsl:with-param name="values" select="/Root/RowSet[
              unit_id = current()/unit_id
            ]" />
          </xsl:call-template>
        </total>
      </Output>
    </xsl:if>
  </xsl:template>

  <xsl:template name="running-total">
    <xsl:param name="values" />
    <xsl:choose>
      <xsl:when test="count($values)">
        <xsl:variable name="curr" select="$values[1]" />
        <xsl:variable name="rest" select="$values[position() &gt; 1]" />
        <!-- recursive step: calculate the total of all remaining values -->
        <xsl:variable name="subtotal">
          <xsl:call-template name="running-total">
            <xsl:with-param name="values" select="$rest" />
          </xsl:call-template>
        </xsl:variable>
        <xsl:value-of select="$subtotal + $curr/price * $curr/VAT" />
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="0" />
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
</xsl:stylesheet>

http://www.xmlplayground.com/NF7D2o

笔记

<xsl:if test="not(preceding-sibling::RowSet/unit_id = current()/unit_id)">是一种对数据进行分组的方法。执行此操作的更高级方法称为 Muenchian 分组,但为了这个答案,我不想深入探讨。

模板在 JavaScript中running-total看起来像这样:

function runningTotal(values) {
  if (values.length) {
    var curr = values.pop();              // pop() shortens values array by one
    var subtotal = runningTotal(values);  // recurses over remaining values
    return subtotal + curr.price * curr.VAT;
  } else {
    return 0;
  }
}
于 2012-11-06T18:41:21.667 回答