使用 XSLT 将 XML 转换为 JSON

Posted

技术标签:

【中文标题】使用 XSLT 将 XML 转换为 JSON【英文标题】:Using XSLT to transform XML to JSON 【发布时间】:2020-01-26 00:10:39 【问题描述】:

我想使用 XSLT 将一些 XML 转换为 JSON。 XML 如下所示:

<DATA_DS>
    <G_1>
        <ORGANIZATION_NAME>My Company 1</ORGANIZATION_NAME>
        <ORGANIZATIONID>901</ORGANIZATIONID>
        <ITEMNUMBER>20001</ITEMNUMBER>
        <ITEMDESCRIPTION>Item Description 1</ITEMDESCRIPTION>
    </G_1>
    <G_1>
        <ORGANIZATION_NAME>My Company 1</ORGANIZATION_NAME>
        <ORGANIZATIONID>901</ORGANIZATIONID>
        <ITEMNUMBER>20002</ITEMNUMBER>
        <ITEMDESCRIPTION>Item Description 2</ITEMDESCRIPTION>
    </G_1>
    <G_1>
        <ORGANIZATION_NAME>My Company 1</ORGANIZATION_NAME>
        <ORGANIZATIONID>901</ORGANIZATIONID>
        <ITEMNUMBER>20003</ITEMNUMBER>
        <ITEMDESCRIPTION>Item Description 3</ITEMDESCRIPTION>
    </G_1>
</DATA_DS>

我希望 JSON 如下所示:

    [
        
            "Item_Number":"20001",
            "Item_Description":"Item Description 1"
        ,
        
            "Item_Number":"20002",
            "Item_Description":"Item Description 2"
        ,
        
            "Item_Number":"20003",
            "Item_Description":"Item Description 3"
        
    ]

推荐的方法是什么?

我正在考虑两种方法:

    尝试使用fn:xml-to-json 函数,定义在https://www.w3.org/TR/xpath-functions-31/#func-xml-to-json。但据我了解,输入 XML 必须遵循以下定义的特定格式:https://www.w3.org/TR/xpath-functions-31/schema-for-json.xsd。而且我还需要输出 JSON 中的字段名称具体为“Item_Number”和“Item_Description”。

    手动编码括号和大括号字符“[”、“]”、“”和“”,以及字段名称“Item_Number”和“Item_Description”。然后使用标准函数列出值并确保正确处理任何特殊字符。例如,“&”字符应该正常出现在 JSON 输出中。

推荐的方法是什么,或者有没有我没有考虑过的更好的方法?

【问题讨论】:

问题标记为 xslt-2.0 - 但 JSON 转换需要 XSLT 3.0。 感谢您指出这一点。我在我的实际环境中尝试了新代码,我确认xml-to-json能够正常运行。 【参考方案1】:

我会采用第一种方法,但首先将给定的输入转换为xml-to-json() 函数所期望的 XML 格式。这可能是这样的:

XSLT 3.0

<xsl:stylesheet version="3.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.w3.org/2005/xpath-functions">
<xsl:output method="text" encoding="UTF-8"/>

<xsl:template match="/G_1">
    <!-- CONVERT INPUT TO XML FOR JSON -->
    <xsl:variable name="xml">
        <array>
            <xsl:for-each-group select="*" group-starting-with="ORGANIZATION_NAME">
                <map>
                    <string key="Item_Number">
                        <xsl:value-of select="current-group()[self::ITEMNUMBER]"/>
                    </string>
                    <string key="Item_Description">
                        <xsl:value-of select="current-group()[self::ITEMDESCRIPTION]"/>
                    </string>
                </map>
            </xsl:for-each-group>
        </array>
    </xsl:variable>
    <!-- OUTPUT -->
    <xsl:value-of select="xml-to-json($xml)"/>
</xsl:template>

</xsl:stylesheet>

演示: https://xsltfiddle.liberty-development.net/bFWR5DQ

【讨论】:

谢谢你,michael.hor257k。实际上,后来我意识到我的原始 XML 只有一组 G_1 标记,并且缺少外部的 DATA_DS 标记。但我能够更改您的解决方案以匹配修改后的 XML 输入。 这是我修改之前的原始 XML:我的公司 190120001项目描述 1 我的公司 190120002项目描述 2我的公司 190120003项目描述3【参考方案2】:

这是采用 michael.hor257k 发布的解决方案并将其应用于我修改后的输入 XML 的结果:

<xsl:stylesheet version="3.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns="http://www.w3.org/2005/xpath-functions">
    <xsl:output method="text" encoding="UTF-8"/>

    <xsl:template match="/DATA_DS">
        <!-- CONVERT INPUT TO XML FOR JSON -->
        <xsl:variable name="xml">
            <array>
                <xsl:for-each  select="G_1">
                <map>
                    <string key="Item_Number">
                        <xsl:value-of select="ITEMNUMBER"/>
                    </string>
                    <string key="Item_Description">
                        <xsl:value-of select="ITEMDESCRIPTION"/>
                    </string>
                </map>
            </xsl:for-each>
            </array>
        </xsl:variable>
        <!-- OUTPUT -->
         <xsl:value-of select="xml-to-json($xml)"/>
    </xsl:template>

</xsl:stylesheet>

【讨论】:

【参考方案3】:

对于像这样的简单映射,您还可以直接构造 XPath 3.1 数组和映射,即在本例中为映射数组:

  <xsl:template match="DATA_DS">
      <xsl:sequence select="array  G_1 ! map  'Item_Number' : string(ITEMNUMBER), 'Item_Description' : string(ITEMDESCRIPTION)  "/>
  </xsl:template>

然后用&lt;xsl:output method="json" indent="yes"/&gt;序列化为JSON:https://xsltfiddle.liberty-development.net/ejivdGS

主要缺点是地图没有顺序,因此您无法控制地图中项目的顺序,例如在该示例中,使用的撒克逊版本 Item_DescriptionItem_Number 之前输出。

但通常转换为xml-to-json 的格式提供了更大的灵活性,并且还允许您控制顺序,因为该函数会保留 JSON 的 XML 表示形式中的顺序。

【讨论】:

请注意,Saxon 有一个扩展名&lt;xsl:output saxon:property-order="Item_Number Item_Description"/&gt;,可用于控制 JSON 序列化期间的属性顺序。 @MichaelKay 属性的顺序对于使用该 JSON 对象的任何系统都没有任何意义——因为映射条目的顺序也没有什么意义并且不能保证。当我们想要保留给定的顺序时,方法是使用数组——在 JSON 对象中或在映射条目的值中 顺序对读取数据的软件没有影响,但对任何人类阅读者都有很大的不同。我添加了这个特性是因为我有一个词汇表,其中对象通常有 9 个简单值属性和 1 个树值属性(很像 XML 中的属性和子项......),如果树 -值属性最后输出。毕竟,我们认识到缩进对于人类可读性的需求,我发现一致的属性顺序对于可读性与缩进同样重要。

以上是关于使用 XSLT 将 XML 转换为 JSON的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 XSLT 将单个子 xml 元素转换为 Json 数组

使用自定义 XSLT 将 XML 转换为 JSON 会丢失花括号

如何使用 xslt 将嵌套的 Json 对象转换为 xml

如何停止 xslt3.0 的 xml-to-json() 函数将数字转换为指数表示法

WSO2 Enterprise Integrator 6.6.0 使用 XSLT 将 XML 响应转换为 Json

xslt 3.0 json-to-xml 和 xml-to-json 转换