【问题标题】:XSLT: convert XML to JSON creating a group of sub nodesXSLT:将 XML 转换为 JSON,创建一组子节点
【发布时间】:2019-04-09 01:46:51
【问题描述】:

尝试执行转换以从 XML 文档中获取 JSON 文件已经有好几天了。我的 xml 文档有不同级别的子节点,我在互联网上找到的所有示例都没有抓住我的情况。 这是我的 xml 示例:

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <foo id="1" group="B" />
    <foo id="2" group="A" />
    <foo id="3", group="A">
        <foo id="4" group="A" />
        <foo id="5" group="A">
            <foo id="6" group="A" />
            <foo id="7" group="A" />
            <foo id="8" group="A" />
        </foo>
    </foo>
    <foo id="9" group="A"></foo>
</root>

所需的 JSON:

{
    "B": {
        "id": 1
    },
    "A": {
        "id": 2
    },
    "A": [{
            "id": 4
        },
        {
            "A": [{
                    "id": 6
                },
                {
                    "id": 7
                },
                {
                    "id": 8
                }
            ]
        }
    ],
    "A": {
        "id": 9
    }
}

意味着每当我嵌套了&lt;foo&gt; 元素时,子元素与父元素组合在一起,等等。

我尝试了一些 xsl 代码(see1see2),但未能使它们适用于我的情况。

【问题讨论】:

  • “我在互联网上找到的所有示例都没有抓住我的案例” - 编程不仅仅是在互联网上找到与您的案例相匹配的示例的问题。您必须了解这些概念,以便根据您的情况调整示例。
  • “所需的 JSON”根本不是 JSON,通过 jsonlint.com 运行它,它会告诉您不允许使用各种 A 键。
  • 还有像&lt;foo id="1", group="B" /&gt;这样的元素开始标签中的所有逗号是做什么用的?这甚至不是格式良好的 XML。

标签: json xml xslt


【解决方案1】:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="xs"
    version="2.0">
    <xsl:strip-space elements="*"/>
    <xsl:output method="xml" indent="yes"/>
    <xsl:template match="root">
        <xsl:text>{</xsl:text>
        <xsl:apply-templates select="//foo"/>
        <xsl:text>}</xsl:text>
    </xsl:template>
    <xsl:template match="//foo">
        <xsl:choose>
            <xsl:when test=" count(ancestor::foo) = 1 and child::foo"/>
            <xsl:when test="foo[child::foo]"/>
            <xsl:otherwise>
                <xsl:text>&quot;</xsl:text><xsl:value-of select="@group"/><xsl:text>&quot;</xsl:text>:<xsl:text> {&#x0A;</xsl:text>
                <xsl:text>      &quot;</xsl:text><xsl:value-of select="@id/name()"/><xsl:text>&quot;</xsl:text>:<xsl:value-of select=" concat(' ',@id)"/>
                <xsl:text>&#x0A; },</xsl:text>
            </xsl:otherwise>
        </xsl:choose>     
    </xsl:template>    
</xsl:stylesheet>

【讨论】:

    【解决方案2】:

    请检查此代码:

        <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" version="2.0">
        <xsl:output method="xml" omit-xml-declaration="yes"/>
        <xsl:template match="root">
            <xsl:text>{</xsl:text>
            <xsl:text>&#x0A;</xsl:text>
            <xsl:for-each select="foo">
                <xsl:if test="not(child::foo)">
                        <xsl:text>    "</xsl:text><xsl:value-of select="@group"/><xsl:text>": </xsl:text><xsl:text>{</xsl:text><xsl:text>&#x0A;</xsl:text>
                        <xsl:text>        "id": </xsl:text><xsl:value-of select="@id"/><xsl:text>&#x0A;</xsl:text>
                        <xsl:text>    }</xsl:text>
                    <xsl:if test="position()!=last()">
                        <xsl:text>,</xsl:text>
                    </xsl:if>
                    <xsl:text>&#x0A;</xsl:text>
                </xsl:if>
                        <xsl:for-each select="foo">
                            <xsl:if test="not(child::foo)">
                                <xsl:text>    "</xsl:text><xsl:value-of select="@group"/><xsl:text>": </xsl:text><xsl:if test="parent::foo"><xsl:text>[</xsl:text></xsl:if><xsl:text>{</xsl:text><xsl:text>&#x0A;</xsl:text>
                            <xsl:text>            "id": </xsl:text><xsl:value-of select="@id"/><xsl:text>&#x0A;</xsl:text>
                                <xsl:text>        },</xsl:text><xsl:text>&#x0A;</xsl:text>
                                <xsl:text>        {</xsl:text><xsl:text>&#x0A;</xsl:text>
                                <xsl:text>        "</xsl:text><xsl:value-of select="@group"/><xsl:text>": </xsl:text><xsl:text>[</xsl:text><xsl:text>&#x0A;</xsl:text>
                            </xsl:if>
    
                               <!-- <xsl:text>        "</xsl:text><xsl:value-of select="@group"/><xsl:text>": </xsl:text><xsl:text>[{</xsl:text><xsl:text>&#x0A;</xsl:text>-->
                            <xsl:for-each select="foo">
                                <xsl:text>              {</xsl:text><xsl:text>&#x0A;</xsl:text>
                                <xsl:text>                 "id": </xsl:text><xsl:value-of select="@id"/><xsl:text>&#x0A;</xsl:text>
                                <xsl:text>              }</xsl:text>
                                <xsl:if test="position()!=last()">
                                    <xsl:text>,</xsl:text>
                                </xsl:if><xsl:text>&#x0A;</xsl:text>
                                <xsl:if test="position()=last()">
                                    <xsl:text>       ]</xsl:text><xsl:text>&#x0A;</xsl:text>
                                </xsl:if>
                                <xsl:if test="position()=last()">
                                    <xsl:text>   }</xsl:text><xsl:text>&#x0A;</xsl:text>
                                </xsl:if>
                                <xsl:if test="position()=last()">
                                    <xsl:text>],</xsl:text><xsl:text>&#x0A;</xsl:text>
                                </xsl:if>
                                <xsl:text>&#x0A;</xsl:text>
                            </xsl:for-each>
                        </xsl:for-each>
            </xsl:for-each>
            <xsl:text>}</xsl:text>
        </xsl:template>
    
    </xsl:stylesheet>
    

    【讨论】:

      【解决方案3】:

      如果有各种 foo group="A" 元素,您想要的结果不是很清楚,因为所需的“JSON”具有重复的属性,但一般来说,在生成 XML 到 JSON 时,您可以使用支持 XPath 3.1 映射和数组的 XSLT 3 (https://www.w3.org/TR/xpath-31/#id-maps-and-arrays) 和json 输出方法或xml-to-json 函数(https://www.w3.org/TR/xslt-30/#json, https://www.w3.org/TR/xslt-30/#func-xml-to-json),这样你就有两种方法来创建 JSON 并输出它,你可以将你的 XML 直接转换为 XPath 3.1 映射/数组并使用&lt;xsl:output method="json"/&gt; 对其进行序列化,或者您可以使用通常的XSLT 处理将您的输入XML 转换为xml-to-json 函数期望的JSON XML 表示,并将其应用于以文本形式获取JSON。

      这是第一种方法的示例:

      <?xml version="1.0" encoding="UTF-8"?>
      <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
          exclude-result-prefixes="#all"
          version="3.0">
      
        <xsl:mode on-no-match="shallow-skip"/>
        <xsl:strip-space elements="*"/>
      
        <xsl:output method="json" indent="yes"/>
      
        <xsl:template match="*[*]">
            <xsl:map>
                <xsl:for-each-group select="foo" group-by="@group">
                    <xsl:map-entry key="string(current-grouping-key())">
                        <xsl:choose>
                            <xsl:when test="not(tail(current-group()))">
                                <xsl:apply-templates select="current-group()"/>
                            </xsl:when>
                            <xsl:otherwise>
                                <xsl:variable name="group-transform" as="map(*)*">
                                    <xsl:apply-templates select="current-group()"/>
                                </xsl:variable>
                                <xsl:sequence select="array { $group-transform }"/>
                            </xsl:otherwise>
                        </xsl:choose>
                    </xsl:map-entry>
                </xsl:for-each-group>
            </xsl:map>
        </xsl:template>
      
      
        <xsl:template match="*[not(*)]">
            <xsl:sequence select="map { 'id' : data(@id) }"/>
        </xsl:template>
      
      </xsl:stylesheet>
      

      https://xsltfiddle.liberty-development.net/6r5Gh3i

      这里是第二个之一:

      <?xml version="1.0" encoding="UTF-8"?>
      <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
          exclude-result-prefixes="#all"
          xmlns="http://www.w3.org/2005/xpath-functions"
          expand-text="yes"
          version="3.0">
      
        <xsl:mode on-no-match="shallow-skip"/>
        <xsl:strip-space elements="*"/>
      
        <xsl:output method="text"/>
      
        <xsl:variable name="json-xml">
            <xsl:apply-templates/>
        </xsl:variable>
      
        <xsl:template match="/">
            <xsl:sequence select="xml-to-json($json-xml, map { 'indent' : true() })"/>
        </xsl:template>
      
        <xsl:template match="*[*]">
            <map>
                <xsl:for-each-group select="foo" group-by="string(@group)">
                    <xsl:choose>
                        <xsl:when test="not(tail(current-group()))">
                            <map key="{current-grouping-key()}">
                                <string key="id">{@id}</string>
                            </map>
                        </xsl:when>
                        <xsl:otherwise>
                            <array key="{current-grouping-key()}">
                                <xsl:apply-templates select="current-group()"/>
                            </array>
                        </xsl:otherwise>
                    </xsl:choose>
                </xsl:for-each-group>          
            </map>
        </xsl:template>
      
        <xsl:template match="*[not(*)]">
            <map>
                <string key="id">{@id}</string>
            </map>
        </xsl:template>
      
      </xsl:stylesheet>
      

      https://xsltfiddle.liberty-development.net/6r5Gh3i/5

      XSLT 3 可使用 Saxon 9.8 或 9.9 HE 用于 Sourceforge 和 Maven https://mvnrepository.com/artifact/net.sf.saxon/Saxon-HE/9.9.1-2 上的 Java 平台以及 NuGet https://www.nuget.org/packages/Saxon-HE/ 上的 .NET 平台。

      【讨论】:

      • 谢谢@Martin 根本不知道这种方法。当我为一个大文件运行您的代码时出现一个问题,输出顺序不正确
      • xml-to-json 的第二种方法应该保留原始 XML 的顺序。不幸的是,创建 XPath 映射的第一种方法无法确保顺序,因为 XPath 映射中的项目没有顺序。
      • 你能发布一份关于你使用的说明的文档吗?我想深入了解一下。提前致谢
      • @mchahoud,我已经添加了相关规范部分的链接。
      猜你喜欢
      • 2019-04-26
      • 1970-01-01
      • 2014-07-16
      • 2018-05-31
      • 1970-01-01
      • 2018-05-27
      • 2013-02-17
      • 2018-12-04
      • 1970-01-01
      相关资源
      最近更新 更多