XSLT 1.0 for-each循环choose变量判断结果不符合预期
XSLT 1.0 XML转换元素生成异常问题
基础信息
使用XSLT 1.0执行XML转换时,遍历节点生成子元素的判断逻辑未按当前遍历节点生效,只要输入文档中存在任意一个Type="A"的Product节点,所有遍历生成的子元素名称均为BoundComponent,不符合预期。
输入XML
<?xml version="1.0" encoding="UTF-8"?> <Project ID="123"> <ProductPool> <Product Type="A" ID="123" DueDate="123" Name="ABC"/> <Product Type="B" ID="123" DueDate="123" Name="ABC"/> <Product Type="A" ID="123" DueDate="123" Name="ABC"/> <Product Type="B" ID="123" DueDate="123" Name="ABC"/> <Product Type="A" ID="123" DueDate="123" Name="ABC"/> </ProductPool> </Project>
原有XSLT代码
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:output method="xml" indent="yes"/> <xsl:template match="/"> <xsl:element name="Product"> <xsl:attribute name="ID"> <xsl:value-of select="//Project/@ID"/> </xsl:attribute> <xsl:variable name="elem-name"> <xsl:choose> <xsl:when test='//Product[@Type="A"]'>BoundComponent</xsl:when> <xsl:otherwise>UnboundComponent</xsl:otherwise> </xsl:choose> </xsl:variable> <xsl:for-each select="//Product"> <xsl:element name="{$elem-name}"> <xsl:attribute name="ID"> <xsl:value-of select="@Name"/> </xsl:attribute> <xsl:attribute name="DueDate"> <xsl:value-of select="@DueDate"/> </xsl:attribute> </xsl:element> </xsl:for-each> </xsl:element> </xsl:template> </xsl:stylesheet>
当前错误输出
<?xml version="1.0"?> <Product ID="123"> <BoundComponent ID="ABC" DueDate="123"/> <BoundComponent ID="ABC" DueDate="123"/> <BoundComponent ID="ABC" DueDate="123"/> <BoundComponent ID="ABC" DueDate="123"/> <BoundComponent ID="ABC" DueDate="123"/> </Product>
预期规则
遍历每个Product节点时,根据节点自身的Type属性判断生成的元素名称:
- Type属性值为
A时,生成BoundComponent元素 - 其余Type属性值时,生成
UnboundComponent元素
问题原因
- 变量
elem-name定义在xsl:for-each循环外部,XSLT中变量是不可变的,只会在定义时计算一次值,不会随着循环内当前遍历节点的切换重新计算 - 变量内的判断条件使用了全局绝对路径
//Product[@Type="A"],该逻辑是判断整个文档中是否存在符合条件的节点,和当前遍历的节点没有关联,只要文档里有一个Type="A"的Product,判断结果就永远为真
修复方案
- 将元素名判断的变量定义移到
xsl:for-each循环内部,让每次遍历节点时都基于当前上下文重新计算变量值 - 修改判断条件,直接读取当前遍历节点的
@Type属性做判断,不再使用全局路径匹配
修复后XSLT代码
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:output method="xml" indent="yes"/> <xsl:template match="/"> <xsl:element name="Product"> <xsl:attribute name="ID"> <xsl:value-of select="//Project/@ID"/> </xsl:attribute> <xsl:for-each select="//Product"> <xsl:variable name="elem-name"> <xsl:choose> <xsl:when test="@Type='A'">BoundComponent</xsl:when> <xsl:otherwise>UnboundComponent</xsl:otherwise> </xsl:choose> </xsl:variable> <xsl:element name="{$elem-name}"> <xsl:attribute name="ID"> <xsl:value-of select="@Name"/> </xsl:attribute> <xsl:attribute name="DueDate"> <xsl:value-of select="@DueDate"/> </xsl:attribute> </xsl:element> </xsl:for-each> </xsl:element> </xsl:template> </xsl:stylesheet>
修复后正确输出
<?xml version="1.0"?> <Product ID="123"> <BoundComponent ID="ABC" DueDate="123"/> <UnboundComponent ID="ABC" DueDate="123"/> <BoundComponent ID="ABC" DueDate="123"/> <UnboundComponent ID="ABC" DueDate="123"/> <BoundComponent ID="ABC" DueDate="123"/> </Product>
内容的提问来源于stack exchange,提问作者Bronko
相关产品推荐
相关产品推荐

