在 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>
元素有什么用途吗?也许在特定情况下?或者这只是多余的?
声明
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"
。