如何使用name()动态应用模板?XSLT转JSON数组元素处理问询
解决XSLT转JSON时动态处理数组元素的问题
我之前也遇到过类似的需求,给你两种实用的解决方案,分场景选择就行:
方案1:预先指定需要转为数组的元素(推荐,可控性强)
如果你明确知道哪些节点是数组容器(比如你的示例里的<tax-lines>),可以先定义一个变量存储这些节点名称,然后针对性处理:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="text" encoding="UTF-8"/> <!-- 定义需要转为数组的容器节点名称 --> <xsl:variable name="array-containers" select="('tax-lines', 'items')"/> <!-- 根节点处理,输出JSON外层结构 --> <xsl:template match="/order"> { <xsl:apply-templates select="*"/> } </xsl:template> <!-- 处理普通节点(非数组容器) --> <xsl:template match="*[not(local-name() = $array-containers)]"> "<xsl:value-of select="local-name()"/>" : "<xsl:value-of select="."/>"<xsl:if test="following-sibling::*">,</xsl:if> </xsl:template> <!-- 处理数组容器节点 --> <xsl:template match="*[local-name() = $array-containers]"> "<xsl:value-of select="local-name()"/>" : [ <xsl:apply-templates select="*"/> ]<xsl:if test="following-sibling::*">,</xsl:if> </xsl:template> <!-- 处理数组内的元素节点 --> <xsl:template match="*[parent::*[local-name() = $array-containers]]"> { <xsl:apply-templates select="*"/> }<xsl:if test="position() != last()">,</xsl:if> </xsl:template> </xsl:stylesheet>
这个方案的好处是你完全可控,想把哪个节点转成数组就加进array-containers变量就行,适合你已经明确数组结构的场景。
方案2:动态判断数组(自动识别多子节点的容器)
如果你的XML结构不固定,需要自动识别“包含多个同名子节点”的容器为数组,可以通过判断子节点数量来实现:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="text" encoding="UTF-8"/> <xsl:template match="/order"> { <xsl:apply-templates select="*"/> } </xsl:template> <!-- 处理普通节点 --> <xsl:template match="*[not(count(*[local-name() = local-name(*[1])]) = count(*))]"> "<xsl:value-of select="local-name()"/>" : "<xsl:value-of select="."/>"<xsl:if test="following-sibling::*">,</xsl:if> </xsl:template> <!-- 处理自动识别的数组容器 --> <xsl:template match="*[count(*[local-name() = local-name(*[1])]) = count(*)]"> "<xsl:value-of select="local-name()"/>" : [ <xsl:apply-templates select="*"/> ]<xsl:if test="following-sibling::*">,</xsl:if> </xsl:template> <!-- 处理数组内的元素 --> <xsl:template match="*[parent::*[count(*[local-name() = local-name(current())]) = count(parent::*)]]"> { <xsl:apply-templates select="*"/> }<xsl:if test="position() != last()">,</xsl:if> </xsl:template> </xsl:stylesheet>
这个方案会自动把所有“子节点名称完全一致”的容器转成数组,适合XML结构灵活多变的场景。
进阶:用XSLT 3.0简化开发
如果你的环境支持XSLT 3.0,直接用内置的JSON序列化功能会更省心,不用手动拼接字符串:
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="json" indent="yes"/> <!-- 自定义数组节点列表 --> <xsl:param name="array-nodes" select="'tax-lines'"/> <xsl:template match="/order"> <xsl:map> <xsl:apply-templates select="*"/> </xsl:map> </xsl:template> <xsl:template match="*"> <xsl:choose> <xsl:when test="local-name() = $array-nodes"> <!-- 转为JSON数组 --> <xsl:map-entry key="concat('''', local-name(), '''')" select="array{*!map{ for $child in * return concat('''', local-name($child), ''': ', if ($child castable as xs:number) then number($child) else concat('''', string($child), '''') ) }}"/> </xsl:when> <xsl:otherwise> <!-- 转为普通键值对 --> <xsl:map-entry key="concat('''', local-name(), '''')" select="if (castable as xs:number) then number(.) else string(.)"/> </xsl:otherwise> </xsl:choose> </xsl:template> </xsl:stylesheet>
你可以根据自己的XSLT版本和业务场景选择对应的方案,测试的时候记得用你提供的<order> XML示例验证一下,调整细节即可。
内容的提问来源于stack exchange,提问作者Souvik04
相关产品推荐
相关产品推荐

