基于XSLT 1.0的XML双次转换:添加小计与统计元素
XSLT 1.0实现XML到XML的双阶段转换
我需要用XSLT 1.0完成XML到XML的转换,必须分至少两个阶段:
- 第一阶段:为每个
<item>计算subtotal(小计) - 第二阶段:基于这些小计生成包含
totalPrice(总价)和totalQuantity(总数量)的<statistics>元素,并将其放在XML结构的顶部
输入XML
<?xml version="1.0" encoding="UTF-8"?> <invoice> <orders> <order id="1"> <lineitem id="1"> <item quantity="2" price="100"/> <item quantity="3" price="50"/> </lineitem> </order> <order id="2"> <lineitem id="1"> <item quantity="5" price="20"/> <item quantity="1" price="100"/> </lineitem> </order> </orders> </invoice>
期望输出XML
<?xml version="1.0" encoding="UTF-8"?> <invoice> <statistics> <totalPrice>550</totalPrice> <totalQuantity>11</totalQuantity> </statistics> <orders> <order id="1"> <lineitem id="1"> <item quantity="2" price="100" subtotal="200"/> <item quantity="3" price="50" subtotal="150"/> </lineitem> </order> <order id="2"> <lineitem id="1"> <item quantity="5" price="20" subtotal="100"/> <item quantity="1" price="100" subtotal="100"/> </lineitem> </order> </orders> </invoice>
问题与解决方案
我已经实现了为<item>添加subtotal的功能,但无法生成<statistics>元素。尝试将processedOrders设为全局变量未成功,最终通过**模式(mode)**实现双阶段转换完成需求,最终XSLT代码如下:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" xmlns:exsl="http://exslt.org/common" extension-element-prefixes="exsl"> <xsl:output method="xml" indent="yes"/> <!-- 双阶段转换 匹配/的模板是转换的入口点。 通过将第一阶段的转换结果存入变量,再对该结果进行第二阶段处理,实现两次遍历输入。 关键:使用模式区分仅在第二阶段执行的逻辑 - 第一阶段(模板内第一次调用<xsl:apply-templates/>)无模式,会执行所有无模式的匹配模板 - 第二阶段(第二次调用<xsl:apply-templates/>)使用"add-statistics"模式,会执行所有无模式模板以及匹配该模式的模板,确保统计元素仅在第二阶段添加,避免重复 --> <xsl:template match="/"> <xsl:variable name="first-transformation"> <xsl:apply-templates/> </xsl:variable> <xsl:apply-templates select="exsl:node-set($first-transformation)/node()" mode="add-statistics"/> </xsl:template> <!-- 生成统计元素 仅在调用模板的模式为"add-statistics"时生成包含总价和总数量的statistics元素。 第一阶段无模式,会跳过该模板;第二阶段使用指定模式,因此会添加该元素,避免重复。 --> <xsl:template match="invoice" mode="add-statistics"> <invoice> <statistics> <totalPrice> <xsl:value-of select="format-number(sum(//item/@subtotal),'0.00')"/> </totalPrice> <totalQuantity> <xsl:value-of select="sum(//item/@quantity)"/> </totalQuantity> </statistics> <xsl:apply-templates/> </invoice> </xsl:template> <!-- 处理订单条目 匹配item的模板会将每个item的price和quantity属性相乘计算小计,然后生成包含price、quantity和计算出的subtotal属性的新item元素。 --> <xsl:template match="item"> <xsl:variable name="subtotal" select="@price * @quantity"/> <item price="{@price}" quantity="{@quantity}" subtotal="{format-number($subtotal,'0.00')}"/> </xsl:template> <!-- 处理orders元素 匹配orders的模板通过累加所有item的subtotal计算总价,然后生成带有totalPrice属性的新orders元素。 --> <xsl:template match="orders"> <orders totalPrice="{format-number(sum(//item/@subtotal),'0.00')}"> <xsl:apply-templates/> </orders> </xsl:template> <!-- 标识模板 这是XSLT中的常用设计模式,用于将源树转换为结果树时,仅修改部分内容,其余内容原样复制。 该模板会复制所有未匹配到其他模板规则的节点和属性。 --> <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> </xsl:stylesheet>
内容的提问来源于stack exchange,提问作者Robert Mark Bram
相关产品推荐
相关产品推荐

