使用XSLT实现行转列并关联对应值的问题求助
解决方案:XSLT转XML为CSV(解决重复值+空值填0)
核心思路
要实现XML转置生成CSV,需分两步处理:
- 提取所有唯一组件类型作为CSV表头
- 遍历每个数据项,匹配对应组件值,无对应组件则填充
0
假设示例XML结构
<root> <item> <name>Item1</name> <components> <component> <type>CompA</type> <value>5</value> </component> <component> <type>CompB</type> <value>3</value> </component> </components> </item> <item> <name>Item2</name> <components> <component> <type>CompA</type> <value>2</value> </component> <component> <type>CompC</type> <value>7</value> </component> </components> </item> </root>
方案1:XSLT 2.0+(简洁版)
利用distinct-values直接获取唯一组件类型,代码更简洁:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="text" encoding="UTF-8"/> <!-- 全局变量:所有不重复的组件类型 --> <xsl:variable name="allComponents" select="distinct-values(/root/item/components/component/type)"/> <!-- 生成CSV表头 + 遍历数据行 --> <xsl:template match="/"> <!-- 表头第一列:Item名称 --> <xsl:text>Item Name</xsl:text> <!-- 追加所有组件类型为表头列 --> <xsl:for-each select="$allComponents"> <xsl:text>,</xsl:text> <xsl:value-of select="."/> </xsl:for-each> <xsl:text> </xsl:text> <!-- 换行 --> <!-- 处理每个数据项 --> <xsl:apply-templates select="/root/item"/> </xsl:template> <!-- 单个数据项处理逻辑 --> <xsl:template match="item"> <!-- 输出Item名称 --> <xsl:value-of select="name"/> <!-- 遍历所有组件类型,匹配当前Item的对应值 --> <xsl:for-each select="$allComponents"> <xsl:text>,</xsl:text> <!-- 存在则取value,不存在填0 --> <xsl:value-of select="if (components/component/type = current()) then components/component[type = current()]/value else '0'"/> </xsl:for-each> <xsl:text> </xsl:text> <!-- 换行 --> </xsl:template> </xsl:stylesheet>
方案2:XSLT 1.0(兼容旧环境)
XSLT 1.0无distinct-values,需用key+generate-id实现去重:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="text" encoding="UTF-8"/> <!-- 定义组件类型的key,用于去重 --> <xsl:key name="componentType" match="component/type" use="."/> <!-- 全局变量:所有不重复的组件类型 --> <xsl:variable name="allComponents" select="/root/item/components/component/type[generate-id() = generate-id(key('componentType', .)[1])]"/> <!-- 生成CSV表头 + 遍历数据行 --> <xsl:template match="/"> <xsl:text>Item Name</xsl:text> <xsl:for-each select="$allComponents"> <xsl:text>,</xsl:text> <xsl:value-of select="."/> </xsl:for-each> <xsl:text> </xsl:text> <xsl:apply-templates select="/root/item"/> </xsl:template> <!-- 单个数据项处理逻辑 --> <xsl:template match="item"> <xsl:value-of select="name"/> <xsl:for-each select="$allComponents"> <xsl:text>,</xsl:text> <xsl:variable name="currentType" select="."/> <xsl:choose> <xsl:when test="components/component/type = $currentType"> <xsl:value-of select="components/component[type = $currentType]/value"/> </xsl:when> <xsl:otherwise>0</xsl:otherwise> </xsl:choose> </xsl:for-each> <xsl:text> </xsl:text> </xsl:template> </xsl:stylesheet>
输出结果示例
Item Name,CompA,CompB,CompC Item1,5,3,0 Item2,2,0,7
问题根源说明
你之前的代码出现重复值,大概率是没有针对每个Item独立查找组件值,而是直接全局获取了第一个Item的组件值,导致所有行复用同一组数据。上述方案通过全局提取组件类型+逐Item匹配的方式,彻底解决了重复问题,同时实现了空值填0的需求。
内容的提问来源于stack exchange,提问作者Arthi Velloori
相关产品推荐
相关产品推荐

