节点的XSLT计算

时间:2019-07-19 09:44:09

标签: xml xslt xslt-2.0

我有以下输入xml:

<Input>
  <Incomes>
    <Income>
      <Year>2016</Year>
      <Amount>10000</Amount>
    </Income>
    <Income>
      <Year>2017</Year>
      <Amount>20000</Amount>
    </Income>
    <Income>
      <Year>2018</Year>
      <Amount>30000</Amount>
    </Income>
  </Incomes>
  <Expenses>
    <Expense>
      <Year>2016</Year>
      <Amount>2000</Amount>
    </Expense>
    <Expense>
      <Year>2017</Year>
      <Amount>5000</Amount>
    </Expense>
    <Expense>
      <Year>2018</Year>
      <Amount>10000</Amount>
    </Expense>
  </Expenses>
</Input>

我想使用xslt将其转换为以下输出xml:

<Output>
  <Savings>
    <Saving>
      <Year>2016</Year>
      <Amount>8000</Amount>
      <!-- Income of 2016 - Expense of 2016 -->
    </Saving>
    <Saving>
      <Year>2017</Year>
      <Amount>15000</Amount>
      <!-- Income of 2017 - Expense of 2017 -->
    </Saving>
    <Saving>
      <Year>2018</Year>
      <Amount>20000</Amount>
    </Saving>
  </Savings>
  <CumulativeSavings>
    <CumulativeSaving>
      <Year>2016</Year>
      <Amount>8000</Amount><!-- Income of 2016 - Expense of 2016 -->
    </CumulativeSaving>
    <CumulativeSaving>
      <Year>2017</Year>
      <Amount>23000</Amount><!-- Income of 2017 - Expense of 2017 + Cumulative Saving of 2016 -->
    </CumulativeSaving>
    <CumulativeSaving>
      <Year>2018</Year>
      <Amount>43000</Amount><!-- Income of 2018 - Expense of 2018 + Cumulative Saving of 2017 -->
    </CumulativeSaving>
  </CumulativeSavings>
</Output>

我正在使用<xsl:for-each select="">遍历所有收入,支出,但是我不确定如何执行收入-支出计算。另外,我需要累积保存,如输出xml中所示。

2 个答案:

答案 0 :(得分:2)

这是一个更通用的解决方案,即使在IncomeExpense元素数量不同的情况下也可以使用-Income在同一年没有Expense匹配,Expense,在同一年内无法与Income匹配,甚至在同一年甚至没有匹配的多个Income和/或Expense元素。

它还使用 FXSL-2 及其函数f:scanl(),该函数计算运行总计(一个人可以阅读有关FXSL herehere的更多信息)。

因此,仅用以下一行代码就可以根据已经计算出的节省量来计算运行总额:

<xsl:variable name="vCumulatives" select="f:scanl1(f:add(), $vSavingsResult/*/Amount)"/>

这是完整的转换(将两个<xsl:import>指令中的@href值替换为您所引用的FXLS样式表的本地路径):

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:f="http://fxsl.sf.net/"
  exclude-result-prefixes="xs f">
  <xsl:import href="file:///C:/CVS-DDN/fxsl-xslt2/f/func-scanl.xsl"/>
  <xsl:import href="file:///C:/CVS-DDN/fxsl-xslt2/f/func-Operators.xsl"/>

 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:key name="kIncomeByYear" match="Income" use="Year"/>
 <xsl:key name="kExpenseByYear" match="Expense" use="Year"/>

  <xsl:template match="node()|@*" mode="#default savings cumulative">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*" mode="#current"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="/*[Incomes/Income | Expenses/Expense]">
    <xsl:variable name="vSavingsResult">
      <xsl:apply-templates mode="savings" select=
        "*/Income[generate-id() = generate-id(key('kIncomeByYear', Year)[1])]
       | */Expense[not(key('kIncomeByYear', Year))
                  and generate-id() = generate-id(key('kExpenseByYear', Year)[1])]">
             <xsl:sort select="Year"/>
       </xsl:apply-templates>
    </xsl:variable>
    <xsl:variable name="vCumulatives" select="f:scanl1(f:add(), $vSavingsResult/*/Amount)"/>

    <Output>
      <Savings>
        <xsl:sequence select="$vSavingsResult/*"/>
      </Savings>
      <CumulativeSavings>
        <xsl:apply-templates select="$vSavingsResult/*" mode="cumulative">
          <xsl:with-param name="pCumulatives" select="$vCumulatives" tunnel="yes"/>
        </xsl:apply-templates>
      </CumulativeSavings>
    </Output>
  </xsl:template>

  <xsl:template match="Income | Expense" mode="savings">
    <Saving>
        <xsl:apply-templates mode="#current"/>
    </Saving>
  </xsl:template>

  <xsl:template match="Amount" mode="savings">
    <Amount>
      <xsl:value-of select=
      "sum(key('kIncomeByYear', ../Year)/Amount) 
     - sum(key('kExpenseByYear', ../Year)/Amount)"/>
    </Amount>
  </xsl:template>

  <xsl:template match="Saving" mode="cumulative">
    <CumulativeSaving><xsl:apply-templates mode="#current"/></CumulativeSaving>
  </xsl:template>

  <xsl:template match="Amount" mode="cumulative">
    <xsl:param name="pCumulatives" tunnel="yes"/>
    <xsl:variable name="vSavingsPos" select="count(../preceding-sibling::*) +1"/>
    <Amount><xsl:value-of select="$pCumulatives[$vSavingsPos]"/></Amount>
  </xsl:template>
</xsl:stylesheet>

此转换应用于提供的XML文档时:

<Input>
  <Incomes>
    <Income>
      <Year>2016</Year>
      <Amount>10000</Amount>
    </Income>
    <Income>
      <Year>2017</Year>
      <Amount>20000</Amount>
    </Income>
    <Income>
      <Year>2018</Year>
      <Amount>30000</Amount>
    </Income>
  </Incomes>
  <Expenses>
    <Expense>
      <Year>2016</Year>
      <Amount>2000</Amount>
    </Expense>
    <Expense>
      <Year>2017</Year>
      <Amount>5000</Amount>
    </Expense>
    <Expense>
      <Year>2018</Year>
      <Amount>10000</Amount>
    </Expense>
  </Expenses>
</Input>

产生想要的正确结果:

<Output>
   <Savings>
      <Saving>
         <Year>2016</Year>
         <Amount>8000</Amount>
      </Saving>
      <Saving>
         <Year>2017</Year>
         <Amount>15000</Amount>
      </Saving>
      <Saving>
         <Year>2018</Year>
         <Amount>20000</Amount>
      </Saving>
   </Savings>
   <CumulativeSavings>
      <CumulativeSaving>
         <Year>2016</Year>
         <Amount>8000</Amount>
      </CumulativeSaving>
      <CumulativeSaving>
         <Year>2017</Year>
         <Amount>23000</Amount>
      </CumulativeSaving>
      <CumulativeSaving>
         <Year>2018</Year>
         <Amount>43000</Amount>
      </CumulativeSaving>
   </CumulativeSavings>
</Output>

这里是一个XML文档,其中包含不匹配的Income元素(2014年),和不匹配的Expense元素(2015年) :

<Input>
  <Incomes>
    <Income>
      <Year>2014</Year>
      <Amount>500</Amount>
    </Income>
    <Income>
      <Year>2016</Year>
      <Amount>10000</Amount>
    </Income>
    <Income>
      <Year>2017</Year>
      <Amount>20000</Amount>
    </Income>
    <Income>
      <Year>2018</Year>
      <Amount>30000</Amount>
    </Income>
  </Incomes>
  <Expenses>
    <Expense>
      <Year>2015</Year>
      <Amount>1000</Amount>
    </Expense>
    <Expense>
      <Year>2016</Year>
      <Amount>2000</Amount>
    </Expense>
    <Expense>
      <Year>2017</Year>
      <Amount>5000</Amount>
    </Expense>
    <Expense>
      <Year>2018</Year>
      <Amount>10000</Amount>
    </Expense>
  </Expenses>
</Input>

对其应用与上面相同的转换会产生正确的结果

<Output>
   <Savings>
      <Saving>
         <Year>2014</Year>
         <Amount>500</Amount>
      </Saving>
      <Saving>
         <Year>2015</Year>
         <Amount>-1000</Amount>
      </Saving>
      <Saving>
         <Year>2016</Year>
         <Amount>8000</Amount>
      </Saving>
      <Saving>
         <Year>2017</Year>
         <Amount>15000</Amount>
      </Saving>
      <Saving>
         <Year>2018</Year>
         <Amount>20000</Amount>
      </Saving>
   </Savings>
   <CumulativeSavings>
      <CumulativeSaving>
         <Year>2014</Year>
         <Amount>500</Amount>
      </CumulativeSaving>
      <CumulativeSaving>
         <Year>2015</Year>
         <Amount>-500</Amount>
      </CumulativeSaving>
      <CumulativeSaving>
         <Year>2016</Year>
         <Amount>7500</Amount>
      </CumulativeSaving>
      <CumulativeSaving>
         <Year>2017</Year>
         <Amount>22500</Amount>
      </CumulativeSaving>
      <CumulativeSaving>
         <Year>2018</Year>
         <Amount>42500</Amount>
      </CumulativeSaving>
   </CumulativeSavings>
</Output>

最后,下面的XML文档除了不匹配的IncomeExpense元素之外,每年还包含多个IncomeExpense元素:< / p>

<Input>
  <Incomes>
    <Income>
      <Year>2014</Year>
      <Amount>100</Amount>
    </Income>
    <Income>
      <Year>2014</Year>
      <Amount>400</Amount>
    </Income>
    <Income>
      <Year>2016</Year>
      <Amount>10000</Amount>
    </Income>
    <Income>
      <Year>2017</Year>
      <Amount>20000</Amount>
    </Income>
    <Income>
      <Year>2018</Year>
      <Amount>30000</Amount>
    </Income>
  </Incomes>
  <Expenses>
    <Expense>
      <Year>2015</Year>
      <Amount>500</Amount>
    </Expense>
    <Expense>
      <Year>2015</Year>
      <Amount>500</Amount>
    </Expense>
    <Expense>
      <Year>2016</Year>
      <Amount>2000</Amount>
    </Expense>
    <Expense>
      <Year>2017</Year>
      <Amount>5000</Amount>
    </Expense>
    <Expense>
      <Year>2018</Year>
      <Amount>10000</Amount>
    </Expense>
  </Expenses>
</Input>

再次对该XML文档应用相同的转换会产生正确的结果

<Output>
   <Savings>
      <Saving>
         <Year>2014</Year>
         <Amount>500</Amount>
      </Saving>
      <Saving>
         <Year>2015</Year>
         <Amount>-1000</Amount>
      </Saving>
      <Saving>
         <Year>2016</Year>
         <Amount>8000</Amount>
      </Saving>
      <Saving>
         <Year>2017</Year>
         <Amount>15000</Amount>
      </Saving>
      <Saving>
         <Year>2018</Year>
         <Amount>20000</Amount>
      </Saving>
   </Savings>
   <CumulativeSavings>
      <CumulativeSaving>
         <Year>2014</Year>
         <Amount>500</Amount>
      </CumulativeSaving>
      <CumulativeSaving>
         <Year>2015</Year>
         <Amount>-500</Amount>
      </CumulativeSaving>
      <CumulativeSaving>
         <Year>2016</Year>
         <Amount>7500</Amount>
      </CumulativeSaving>
      <CumulativeSaving>
         <Year>2017</Year>
         <Amount>22500</Amount>
      </CumulativeSaving>
      <CumulativeSaving>
         <Year>2018</Year>
         <Amount>42500</Amount>
      </CumulativeSaving>
   </CumulativeSavings>
</Output>

答案 1 :(得分:1)

考虑使用键查找费用项目

<xsl:key name="expenses" match="Expense" use="Year" />

然后,对于给定的Income元素,您可以像这样获得量

<xsl:variable name="currentAmount" select="Amount - key('expenses', Year)/Amount" />

就获取累计总数而言,您可以使用递归模板一次获取Income元素,并使用参数将当前值传递给下一个调用,因此,您可以输出SavingCumulativeSaving元素。如果将此结果存储在变量中,则可以分别输出它们。

尝试使用此XSLT:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
  <xsl:key name="expenses" match="Expense" use="Year" />

  <xsl:output method="xml" indent="yes"/>

  <xsl:template match="/">
    <xsl:variable name="calcs">
      <xsl:apply-templates select="(//Income)[1]" />
    </xsl:variable>
    <Output>
      <Savings>
        <xsl:copy-of select="$calcs/Saving" />
      </Savings>
      <CumulativeSavings>
        <xsl:copy-of select="$calcs/CumulativeSaving" />
      </CumulativeSavings>
    </Output>
  </xsl:template>

  <xsl:template match="Income">
    <xsl:param name="previousAmount" select="0" />
    <xsl:variable name="currentAmount" select="Amount - key('expenses', Year)/Amount" />
    <xsl:variable name="newAmount" select="$currentAmount + $previousAmount" />
    <Saving>
      <xsl:copy-of select="Year" />
      <Amount>
        <xsl:value-of select="$currentAmount" />
      </Amount>
    </Saving>
    <CumulativeSaving>
      <xsl:copy-of select="Year" />
      <Amount>
        <xsl:value-of select="$newAmount" />
      </Amount>
    </CumulativeSaving>
    <xsl:apply-templates select="following-sibling::Income[1]">
      <xsl:with-param name="previousAmount" select="$newAmount" />
    </xsl:apply-templates>
  </xsl:template>
</xsl:stylesheet>

请注意,在XSLT 3.0中,您可以使用xsl:iterate代替递归模板。参见http://xsltfiddle.liberty-development.net/pPzifqk