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>

for-each サイクルで 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

非再帰的な 2 パス ソリューションは次のとおりです。

<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. 2 番目のパスでは、古典的な Muenchian のグループ化を実行し、各グループで子を合計し、計算された合計が text-node 子で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 に答える