XSLT全局变量使用问题:JSON转XML行级元素未填充
解决XSLT JSON转XML中订单级变量无法在行级for-each填充的问题
问题本质
XSLT的变量是作用域绑定的,你遇到的问题大概率是两个原因:要么是订单级变量定义时没正确捕获订单节点的上下文,要么是json-to-xml()转换后的节点路径写错了,导致变量本身就是空值,自然在行级for-each里输出不了内容。另外,如果你用的是旧版XSLT处理器,也可能存在JSON处理的兼容性问题。
示例代码分析
先假设你的原始JSON和出错的XSLT是这样的(如果和你的实际代码不符,可以对照调整):
示例JSON
{ "Order": { "OrderID": "ORD001", "Customer": "Alice", "Lines": [ {"LineID": "L001", "Product": "Book"}, {"LineID": "L002", "Product": "Pen"} ] } }
出错的XSLT(常见错误写法)
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" indent="yes"/> <xsl:template match="/"> <Root> <xsl:apply-templates select="json-to-xml(.)/*[@key='Order']"/> </Root> </xsl:template> <xsl:template match="*[@key='Order']"> <!-- 定义订单级变量 --> <xsl:variable name="orderID" select="*[@key='OrderID']/text()"/> <xsl:variable name="customer" select="*[@key='Customer']/text()"/> <Order> <OrderID><xsl:value-of select="$orderID"/></OrderID> <Customer><xsl:value-of select="$customer"/></Customer> <!-- 行级循环 --> <xsl:for-each select="*[@key='Lines']/*"> <Line> <LineID><xsl:value-of select="*[@key='LineID']/text()"/></LineID> <Product><xsl:value-of select="*[@key='Product']/text()"/></Product> <!-- 这里输出为空 --> <OrderRef><xsl:value-of select="$orderID"/></OrderRef> <CustomerRef><xsl:value-of select="$customer"/></CustomerRef> </Line> </xsl:for-each> </Order> </xsl:template> </xsl:stylesheet>
这个写法的问题:json-to-xml()转换后的节点,文本内容直接在元素里,不需要加/text(),而且如果循环时上下文切换后,变量本身可能因为路径错误是空的,或者你没正确保存订单节点的上下文。
解决办法
办法1:正确绑定订单节点上下文(基于XML节点处理)
先把整个订单节点保存到变量里,后续不管上下文怎么切换,都通过这个变量访问订单级数据,同时修正节点选择路径:
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" indent="yes"/> <xsl:template match="/"> <Root> <xsl:apply-templates select="json-to-xml(.)/*[@key='Order']"/> </Root> </xsl:template> <xsl:template match="*[@key='Order']"> <!-- 保存当前订单节点的上下文 --> <xsl:variable name="orderNode" select="."/> <Order> <OrderID><xsl:value-of select="$orderNode/*[@key='OrderID']"/></OrderID> <Customer><xsl:value-of select="$orderNode/*[@key='Customer']"/></Customer> <!-- 通过保存的orderNode定位行数据 --> <xsl:for-each select="$orderNode/*[@key='Lines']/*"> <Line> <LineID><xsl:value-of select="*[@key='LineID']"/></LineID> <Product><xsl:value-of select="*[@key='Product']"/></Product> <!-- 直接通过orderNode访问订单级数据 --> <OrderRef><xsl:value-of select="$orderNode/*[@key='OrderID']"/></OrderRef> <CustomerRef><xsl:value-of select="$orderNode/*[@key='Customer']"/></CustomerRef> </Line> </xsl:for-each> </Order> </xsl:template> </xsl:stylesheet>
办法2:直接操作JSON对象(XSLT 3.0推荐写法)
不用把JSON转成XML节点,直接用parse-json()解析成map/array,操作更直观,也不会有上下文切换的问题:
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" indent="yes"/> <xsl:template match="/"> <!-- 解析JSON为map对象 --> <xsl:variable name="jsonData" select="parse-json(.)"/> <Root> <Order> <OrderID><xsl:value-of select="$jsonData?Order?OrderID"/></OrderID> <Customer><xsl:value-of select="$jsonData?Order?Customer"/></Customer> <!-- 遍历订单的Lines数组 --> <xsl:for-each select="$jsonData?Order?Lines?*"> <Line> <LineID><xsl:value-of select="?LineID"/></LineID> <Product><xsl:value-of select="?Product"/></Product> <!-- 直接访问外层的订单数据 --> <OrderRef><xsl:value-of select="$jsonData?Order?OrderID"/></OrderRef> <CustomerRef><xsl:value-of select="$jsonData?Order?Customer"/></CustomerRef> </Line> </xsl:for-each> </Order> </Root> </xsl:template> </xsl:stylesheet>
额外检查项
- 先用
json-to-xml(.)输出转换后的XML结构,确认节点的@key属性和你写的选择器完全匹配,比如有没有大小写错误。 - 确保你用的是支持XSLT 3.0的处理器(比如Saxon HE/PE),旧版处理器对JSON的支持有限。
- 变量要定义在for-each循环之前的作用域里,确保循环内可以访问。
内容的提问来源于stack exchange,提问作者ChrisP
相关产品推荐
相关产品推荐

