如何在XSLT中创建组合节点列表(无需for-each循环)
XSLT处理XML时补充缺失的process节点
输入XML
<all> <list1> <name>x</name> <name>y</name> </list1> <list2> <process> <value>1</value> <value>2</value> </process> <process> <value>1</value> <value>2</value> <value>3</value> </process> <process> <value>1</value> </process> </list2> </all>
预期输出
<list3> <object> <process> <name>x</name> <value>1</value> </process> <process> <name>y</name> <value>2</value> </process> </object> <object> <process> <name>x</name> <value>1</value> </process> <process> <name>y</name> <value>2</value> </process> <process> <value>3</value> </process> </object> <object> <process> <name>x</name> <value>1</value> </process> <process> <value>2</value> </process> <process> <value>3</value> </process> </object> </list3>
当前使用的XSLT代码
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xalan="xml.apache.org/xalan"> <xsl:output method="xml" indent="yes" xalan:indent-amount="4"/> <xsl:strip-space elements="*"/> <!--Identity Trasnform --> <xsl:template match="node()|@*"> <xsl:copy> <xsl:apply-templates select="node() |@*"/> </xsl:copy> </xsl:template> <xsl:template match="all"> <list3> <xsl:apply-templates select="/all/list2/process"/> </list3> </xsl:template> <xsl:template match="process"> <object> <xsl:apply-templates select="*"/> </object> </xsl:template> <xsl:template match="value"> <xsl:variable name="pos" select="position()"/> <process> <xsl:if test="//list1/name[$pos]"> <name><xsl:value-of select="//list1/name[$pos]"/></name> </xsl:if> <value><xsl:value-of select="."/></value> </process> </xsl:template> </xsl:stylesheet>
实际输出
<list3> <object> <process> <name>x</name> <value>1</value> </process> <process> <name>y</name> <value>2</value> </process> </object> <object> <process> <name>x</name> <value>1</value> </process> <process> <name>y</name> <value>2</value> </process> <process> <value>3</value> </process> </object> <object> <process> <name>x</name> <value>1</value> </process> </object> </list3>
问题分析
当前XSLT仅遍历process下已有的value节点生成process元素,当value数量少于list1/name的数量,或需要生成的process数量超过原value数量时,无法自动补充对应位置的节点,导致输出不符合预期。
修正后的XSLT代码
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xalan="xml.apache.org/xalan"> <xsl:output method="xml" indent="yes" xalan:indent-amount="4"/> <xsl:strip-space elements="*"/> <!-- 全局变量存储list1的name列表和所有process的最大value数量 --> <xsl:variable name="name-list" select="/all/list1/name"/> <xsl:variable name="max-value-num" select="max(/all/list2/process/count(value))"/> <!-- 替换根节点all为list3 --> <xsl:template match="all"> <list3> <xsl:apply-templates select="list2/process"/> </list3> </xsl:template> <!-- 每个process生成一个object --> <xsl:template match="process"> <object> <!-- 遍历1到最大value数量的序号,生成对应process元素 --> <xsl:apply-templates select="1 to $max-value-num"> <xsl:with-param name="current-process" select="."/> </xsl:apply-templates> </object> </xsl:template> <!-- 处理每个序号,生成对应的process节点 --> <xsl:template match=".[. instance of xs:integer]"> <xsl:param name="current-process"/> <xsl:variable name="index" select="."/> <process> <!-- 若当前序号在name列表范围内,添加name元素 --> <xsl:if test="$name-list[$index]"> <name><xsl:value-of select="$name-list[$index]"/></name> </xsl:if> <!-- 优先使用原process中对应位置的value,无则用序号填充 --> <value> <xsl:choose> <xsl:when test="$current-process/value[$index]"> <xsl:value-of select="$current-process/value[$index]"/> </xsl:when> <xsl:otherwise> <xsl:value-of select="$index"/> </xsl:otherwise> </xsl:choose> </value> </process> </xsl:template> <!-- 禁用默认身份转换,避免处理无关节点 --> <xsl:template match="node()|@*"/> </xsl:stylesheet>
内容的提问来源于stack exchange,提问作者asb
相关产品推荐
相关产品推荐

