如何在XSLT转换中复用父节点值?实现嵌套XML转扁平CSV
深度嵌套XML转扁平CSV的XSLT正确实现
嘿,我看你已经摸到门路了,但在循环逻辑和父节点值的复用绑定上还有点小问题,咱们一步步来解决这个需求:
你的核心需求是:遍历到最底层的<stock>节点(属于<availability>),同时把上层<category>、<element>的字段值复制到每一行CSV里,每个<stock>对应一行,对吧?
推荐的XSLT实现(XSLT 3.0)
这里给你两种可行的写法,都是围绕“让每个<stock>生成一行,同时带上所有父节点数据”这个核心逻辑:
方案一:从最内层节点向上回溯(更简洁)
这种写法直接从最底层的<stock>入手,通过轴向上查找父节点的值,逻辑清晰且代码简洁:
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="text" omit-xml-declaration="yes" indent="no"/> <!-- 每个<stock>生成一行CSV --> <xsl:template match="stock"> <!-- 获取顶层category的type值 --> <xsl:value-of select="ancestor::market/category/type"/> <xsl:text>,</xsl:text> <!-- 获取当前stock所属element的字段 --> <xsl:value-of select="ancestor::element/(name, type, color)" separator=","/> <xsl:text>,</xsl:text> <!-- 获取当前stock的属性 --> <xsl:value-of select="@cat, @in" separator=","/> <xsl:text> </xsl:text> <!-- 换行符 --> </xsl:template> <!-- 忽略XML中的空白文本,避免输出冗余内容 --> <xsl:template match="text()"/> </xsl:stylesheet>
方案二:嵌套循环(和你的思路更贴近)
如果你更习惯从外层节点开始遍历,也可以用嵌套循环的方式,先遍历<element>,再遍历每个<element>下的<stock>,同时把父节点的值存到变量里复用:
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="text" omit-xml-declaration="yes" indent="no"/> <xsl:template match="market"> <!-- 先存好顶层category的type值,后续复用 --> <xsl:variable name="category-type" select="category/type"/> <!-- 遍历每个element --> <xsl:for-each select="items/element"> <!-- 存当前element的所有字段,和category值拼在一起 --> <xsl:variable name="element-data" select="$category-type, name, type, color" separator=","/> <!-- 遍历当前element下的每个stock,生成一行 --> <xsl:for-each select="availability/stock"> <xsl:value-of select="$element-data, @cat, @in" separator=","/> <xsl:text> </xsl:text> </xsl:for-each> </xsl:for-each> </xsl:template> </xsl:stylesheet>
为什么你的初始代码没生效?
咱们来分析下你最初的代码问题:
- 你用了三个独立的
for-each,分别遍历category、element、stock,这会导致所有category值先一次性输出,然后是所有element值,最后是所有stock值,完全没有把父节点和子节点的数据对应起来,自然得不到每行关联的结果。
而你提到的第二段代码其实方向是对的,但有几个小瑕疵:
$ctype的路径写错了:market/category/type应该改成category/type(因为当前模板匹配的是market,直接访问子节点即可)- 用
copy-of(.)!(.//name, .//type, .//color)有点冗余,直接用name, type, color就可以 - 开头多了个
;,会导致每行第一个字段前有多余的分隔符
测试结果
用你提供的示例XML测试,两种方案都会输出符合预期的CSV:
kids,police car,toy,blue,A,5 kids,police car,toy,blue,B,2
内容的提问来源于stack exchange,提问作者membersound
相关产品推荐
相关产品推荐

