XSLT 3.0 将 JSON 处理为 XML:从未调用过数组模板 - 优先级规则不清楚

问题描述 投票:0回答:1

在 XSLT 3.0 中,我想以标准方式处理通过从包含对象和简单值数组的 JSON 输入调用

json-to-xml()
隐式生成的 XML。中给出的解决方案 使用 XSLT 3.0 函数将 JSON 转换为 XML XSLT 3.0 中的 JSON 到 XML 转换 看起来很简单。然而,数组的模板没有被调用。尽管事实上,以第二个解决方案为例,其数组模板规则的匹配条件,即

<xsl:template match="array">

<xsl:template match="array[@key]/*">

比第一个模板规则更具体,即

<xsl:template match="*[@key]">
。 我似乎记得 XSLT 优先级规则的一般经验法则是:“更具体的规则胜过更一般的规则。”

举一个我自己的例子,它受到上述解决方案的启发,在下面的样式表中,模板

<xsl:template match="array[@key='phones']">
和模板
<xsl:template match="array[@key]">
都没有被调用:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0"
                xmlns:fn="http://www.w3.org/2005/xpath-functions"
                exclude-result-prefixes="fn"> 

   <xsl:param name="json">
   { 
    "person":{
        "name": "John",
        "age": 30,
        "address": {
          "city": "New York",
          "zipcode": "10001"
        },
        "phones": ["123-4567", "987-6543"]
      }
   }

      <!-- A reminder of what would be produced by a call to <xsl:copy-of select="$XMLfromJSON" />:
<map xmlns="http://www.w3.org/2005/xpath-functions">
    <map key="person">
        <string key="name">John</string>
        <number key="age">30</number>
        <map key="address">
            <string key="city">New York</string>
            <string key="zipcode">10001</string>
        </map>
        <array key="phones">
            <string>123-4567</string>
            <string>987-6543</string>
        </array>
    </map>
</map>
-->
</xsl:param>
    <xsl:variable name="XMLfromJSON" select="json-to-xml($json)" />
   <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>

   <xsl:template match="/">
        <xsl:apply-templates select="$XMLfromJSON/*"/>
   </xsl:template>

    <!-- Universal template for processing elements with an attribute "key"-->
    <xsl:template match="*[@key]">
        <xsl:element name="{@key}">
            <xsl:apply-templates/>
        </xsl:element>
    </xsl:template>

    <!-- Specific template for processing arrays named "phones" -->
    <xsl:template match="array[@key='phones']">
        <xsl:element name="{@key}">
            <xsl:for-each select="*">
                <value>
                    <xsl:value-of select="."/>
                </value>
            </xsl:for-each>
        </xsl:element>
    </xsl:template>

    <!-- Universal fallback-template for processing other arrays -->
    <xsl:template match="array[@key]">
        <xsl:element name="{@key}">
            <xsl:for-each select="*">
                <item>
                    <xsl:apply-templates/>
                </item>
            </xsl:for-each>
        </xsl:element>
    </xsl:template>
    
</xsl:stylesheet>

这只会产生以下 XML:

<person>
    <name>John</name>
    <age>30</age>
    <address>
        <city>New York</city>
        <zipcode>10001</zipcode>
    </address>
    <phones>123-4567987-6543</phones>
</person>

但是我想要的是以下 XML:

<person>
    <name>John</name>
    <age>30</age>
    <address>
        <city>New York</city>
        <zipcode>10001</zipcode>
    </address>
    <phones>
        <value>123-4567</value>
        <value>987-6543</value>
    </phones>
</person>

所以我的第一个问题是:为什么数组模板没有被调用?谁能给我解释一下吗?也许参考 XSLT 3.0 优先规则的具体子规则?

我的第二个问题是:如何让这些模板被自动调用?

如果这是不可能的我的第三个问题是:这里最优雅(特别是模块化)的解决方案是什么?

我想出了一个产生所需输出的解决方案,即:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0"
                xmlns:fn="http://www.w3.org/2005/xpath-functions"
                exclude-result-prefixes="fn"> 

   <xsl:param name="json">
   { 
    "person":{
        "name": "John",
        "age": 30,
        "address": {
          "city": "New York",
          "zipcode": "10001"
        },
        "phones": ["123-4567", "987-6543"]
      }
   }

      
</xsl:param>
    <xsl:variable name="XMLfromJSON" select="json-to-xml($json)" />
   <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>

   <xsl:template match="/">
        <xsl:apply-templates select="$XMLfromJSON/*"/>
   </xsl:template>

    <xsl:template match="*[@key]" >
        <xsl:element name="{@key}">
            <xsl:choose>
                <xsl:when test="name() = 'array'">
                    <xsl:call-template name="process-array"/>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:apply-templates/>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:element>
    </xsl:template>
    
    <!-- Named template for processing arrays -->
    <xsl:template name="process-array">
        <xsl:for-each select="*">
            <value>
                <xsl:apply-templates/>
            </value>
        </xsl:for-each>
    </xsl:template>
    
</xsl:stylesheet>

但是,我真的希望避免任何条件代码,例如 xsl:choose 或 xsl:if 和命名模板,而是依赖 XSLT 的处理器规则。

作为可选的第四个问题,出于兴趣:我上面引用的第一个链接,即 使用 XSLT 3.0 函数将 JSON 转换为 XML

使用了

<xsl:copy>
元素,如下所示:

<xsl:copy>
        <xsl:apply-templates select="json-to-xml(.)/*"/>
</xsl:copy>

但是,我把它删除了,结果还是一样。

<xsl:copy>
元素也没有出现在上面第二个链接的非常相似的解决方案中。 这个
<xsl:copy>
元素有什么用途吗?也许在特定情况下?或者这只是多余的?

json xml xslt xslt-3.0
1个回答
0
投票

声明

xmlns:fn="http://www.w3.org/2005/xpath-functions"
后,您可以在
fn:array
上进行匹配。如果您希望不合格的
match="array"
与 JSON 的 XML 表示形式中的元素匹配(这些元素位于命名空间中),那么您可以在 XSLT 中使用
xpath-default-namespace="http://www.w3.org/2005/xpath-functions"

© www.soinside.com 2019 - 2024. All rights reserved.