xsl转换

时间:2010-08-26 13:39:38

标签: xml xslt

我是xsl转换的新手,我有一个问题。 我循环遍历这样的xml:

 <PO>
<Items>
  <Item>
     <Price>2</Price>
     <Quantity>5</Quantity>
  </Item>
  <Item>
     <Price>3</Price>
     <Quantity>2</Quantity>
  </Item>    
 </Items>
 <QuantityTotal></QuantityTotal>
 </PO>

现在我想在QuantityTotal节点中插入一个值:
该值是所有项目的价格*数量的总和,在这种情况下(2 * 5)+(3 * 2)= 16 我怎么能这样做,我尝试了循环和变量,但变量是不可变的,所以我不知道如何实现这一点。

感谢您的帮助

3 个答案:

答案 0 :(得分:4)

这是一个XSLT解决方案 - 无需扩展功能

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

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

 <xsl:template match="QuantityTotal">
  <xsl:copy>
   <xsl:call-template name="sumProducts">
     <xsl:with-param name="pNodes" select="../Items/Item"/>
   </xsl:call-template>
  </xsl:copy>
 </xsl:template>

 <xsl:template name="sumProducts">
  <xsl:param name="pNodes"/>
  <xsl:param name="pSum" select="0"/>
  <xsl:param name="pEname1" select="'Price'"/>
  <xsl:param name="pEname2" select="'Quantity'"/>

  <xsl:choose>
   <xsl:when test="not($pNodes)">
    <xsl:value-of select="$pSum"/>
   </xsl:when>
  <xsl:otherwise>
    <xsl:call-template name="sumProducts">
      <xsl:with-param name="pNodes" select=
      "$pNodes[position() > 1]"/>
      <xsl:with-param name="pSum" select=
      "$pSum
      +
       $pNodes[1]/*[name()=$pEname1]
      *
       $pNodes[1]/*[name()=$pEname2]
       "/>
    </xsl:call-template>
  </xsl:otherwise>
  </xsl:choose>
 </xsl:template>
</xsl:stylesheet>

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

<PO>
    <Items>
        <Item>
            <Price>2</Price>
            <Quantity>5</Quantity>
        </Item>
        <Item>
            <Price>3</Price>
            <Quantity>2</Quantity>
        </Item>
    </Items>
    <QuantityTotal></QuantityTotal>
</PO>

生成了想要的结果

<PO>
   <Items>
      <Item>
         <Price>2</Price>
         <Quantity>5</Quantity>
      </Item>
      <Item>
         <Price>3</Price>
         <Quantity>2</Quantity>
      </Item>
   </Items>
   <QuantityTotal>16</QuantityTotal>
</PO>

答案 1 :(得分:2)

除了Dimitre的优秀答案外,这个样式表采用了其他方法:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="QuantityTotal">
        <xsl:copy>
            <xsl:apply-templates select="../Items/Item[1]" mode="sum"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="Item" mode="sum">
        <xsl:param name="pSum" select="0"/>
        <xsl:variable name="vNext" select="following-sibling::Item[1]"/>
        <xsl:variable name="vSum" select="$pSum + Price * Quantity"/>
        <xsl:apply-templates select="$vNext" mode="sum">
            <xsl:with-param name="pSum" select="$vSum"/>
        </xsl:apply-templates>
        <xsl:if test="not($vNext)">
            <xsl:value-of select="$vSum"/>
        </xsl:if>
    </xsl:template>
</xsl:stylesheet>

输出:

<PO>
    <Items>
        <Item>
            <Price>2</Price>
            <Quantity>5</Quantity>
        </Item>
        <Item>
            <Price>3</Price>
            <Quantity>2</Quantity>
        </Item>
    </Items>
    <QuantityTotal>16</QuantityTotal>
</PO>

答案 2 :(得分:1)

这是使用XSLT2的解决方案,其中节点集是第一类对象。在XSLT1中,您需要使用节点集扩展。

以下说明:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:xs="http://www.w3.org/2001/XMLSchema" version="2.0">

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

    <xsl:variable name="extendedItems" as="xs:integer*">
        <xsl:for-each select="//Item">
             <xsl:value-of select="./Price * ./Quantity"/>
        </xsl:for-each>
    </xsl:variable>

    <xsl:variable name="total">
        <xsl:value-of select="sum($extendedItems)"/>
    </xsl:variable>

    <xsl:template match="//QuantityTotal">
        <xsl:copy>
            <xsl:apply-templates select="@*"/>
            <xsl:value-of select="$total"/>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

这里的方法是使用“身份转换”来复制文档,同时执行计算并将结果插入到输出QuantityTotal模板中。第一个模板将输入复制到输出,但被底部的QuantityTotal更具体的模板覆盖。第一个变量声明创建扩展成本列表,第二个变量定义汇总生成总计的成本。然后将总数插入QuantityTotal节点。

理解XSL的关键在于它本质上是声明性的。几乎所有初学者最常见的概念错误是假设样式表是一个处理输入XML文档的顺序程序。实际上,这是相反的方式。 XSL引擎读取XML文档。并且对于它遇到的每个新标签,它在样式表中查找“最佳”匹配,执行该模板。

编辑:

这是一个适用于Saxon 6.5的xslt1.1版本

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:ex="http://exslt.org/common"
    extension-element-prefixes="ex"
    version="1.1">
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    <xsl:variable name="extendedItems">
        <xsl:for-each select="//Item">
            <extended>
             <xsl:value-of select="./Price * ./Quantity"/>
            </extended>
            </xsl:for-each>
    </xsl:variable>
    <xsl:variable name="total">
        <xsl:value-of select="sum(ex:node-set($extendedItems/extended))"/>
    </xsl:variable>
    <xsl:template match="//QuantityTotal">
        <xsl:copy>
            <xsl:apply-templates select="@*"/>
            <xsl:value-of select="$total"/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>