如何使用 MarkLogic XSLT 修改一个 JSON 属性?
How do I modify one JSON property using MarkLogic XSLT?
我有一些 JSON、一个 XPath 和一个值。我想用新值替换 XPath 指向的 JSON 属性 中的现有值。我想我可以用 XSLT 来做,但我不太擅长 XSLT。这将在 XQuery 模块中。
对于XML,我可以这样做:
let $content :=
document {
<class>
<student rollno = "393">
<firstname>Dinkar</firstname>
<lastname>Kad</lastname>
<nickname>Dinkar</nickname>
<marks>85</marks>
</student>
<student rollno = "493">
<firstname>Vaneet</firstname>
<lastname>Gupta</lastname>
<nickname>Vinni</nickname>
<marks>95</marks>
</student>
</class>
}
let $template :=
<xsl:stylesheet version = "1.0"
xmlns:xsl = "http://www.w3.org/1999/XSL/Transform">
<xsl:template match = "node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="student/marks">
<foo>bar</foo>
</xsl:template>
</xsl:stylesheet>
return
xdmp:xslt-eval($template, $content)
用 <foo>bar</foo>
元素正确替换了 class/student/marks
元素。
对于 JSON,我正在尝试这样做:
let $stuff :=
document {
object-node {
"SomeProperty": object-node {
"LowProperty1":"some string",
"LowProperty2":"some string",
"LowProperty3": array-node { "some string 1", "some string 2"}
}
}
}
let $target := xdmp:unpath("/EvenLowerProperty/LowestProperty1", (), $stuff)
return
xdmp:xslt-eval(
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0"
xmlns:json="http://marklogic.com/xdmp/json">
<xsl:template match="node()">
<xsl:copy>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="SomeProperty/LowProperty1">
{
map:entry("LowProperty1", "bar")
}
</xsl:template>
</xsl:stylesheet>,
$stuff
)
我想这样结束:
{
"SomeProperty": {
"LowProperty1":"bar",
"LowProperty2":"some string",
"LowProperty3": [ "some string 1", "some string 2" ]
}
}
相反,我得到了原件的副本。我已经尝试了一些变体,但我并没有更接近。我应该期望它起作用吗?
除非 MarkLogic 做了一些我不知道的事情来扩展标准 XSLT 语义,否则这是行不通的。 SomeProperty/LowProperty1
之类的匹配模式不能用于寻址 map/array 树的部分地址。你可以在这样的树中匹配东西,但它不是很有用,因为匹配不能是上下文敏感的:给定一个映射或数组,你无法找到它在哪里或如何到达那里。
您可能会发现阅读我的 XML 布拉格 2016 年关于使用 XSLT 3.0 转换 JSON 的论文很有用:http://www.saxonica.com/papers/xmlprague-2016mhk.pdf
使用 XSLT 模板匹配转换 XML 的标准方法不能很好地转换为 JSON,根本原因是 map/array 结构用于表示 JSON 没有 "node identity" 或向上导航(父指针)。在我论文中的示例中,我通常发现进行这种转换的最简单方法是将结构转换为 XML,转换 XML,然后再转换回来——尽管还有其他方法你可能会考虑。
我一直在尝试设计一种高阶扩展函数来简化此类任务。我认为我还没有理想的解决方案。
问题似乎是 MarkLogic 的 XSLT 处理器处理 JSON 扩展的程度与其 XQuery 处理器不同。 <xsl:copy>
似乎被 object-node()
短路,而不是仅复制上下文节点,其行为类似于 <xsl:copy-of>
,复制所有后代,这会阻止 LowProperty1
模板(以及任何其他模板)从执行。您可以通过向 LowProperty1
模板添加一个 <xsl:message>
来确认这一点,并看到该消息从未被记录。
据我所知,没有惯用的方法可以从 XSLT 中复制 JSON 节点。因此,另一种方法是在转换前后简单地转换 to/from json:object
- 当然这可以在 运行 XSLT 之前在 XQuery 中完成(这可能更可取)。
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" version="2.0"
xmlns:json="http://marklogic.com/xdmp/json"
xmlns:xdmp="http://marklogic.com/xdmp">
<xsl:template match="document-node()">
<xsl:variable name="jsonxml" as="element()">
<temp><xsl:sequence select="xdmp:from-json(.)"/></temp>
</xsl:variable>
<xsl:variable name="result" as="element(json:object)">
<xsl:apply-templates select="$jsonxml/json:object"/>
</xsl:variable>
<xsl:sequence select="xdmp:to-json(json:object($result))"/>
</xsl:template>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="json:entry[@key='LowProperty1']/json:value">
<xsl:copy>
<xsl:text>bar</xsl:text>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
如果设置 xdmp:dialect="1.0-ml"
,则可以对 JSON 节点类型使用模板匹配模式:object-node()
、array-node()
、number-node()
、boolean-node()
, null-node()
除了能够使用 XPath 和基于节点名称的匹配模式,例如 SomeProperty/LowProperty1
.
不幸的是,xsl:copy
执行深度复制,这使得转换变得困难,并且没有可用于这些 JSON 节点的 XSLT 节点构造器。
因此,将 JSON 转换为 XML、HTML 和文本非常容易,但是为了构建您想要的转换后的 JSON,您将要么像@wst 演示的那样转换 to/from json:object
,要么你可以稍微作弊,只生成 JSON.
的文本
使用一些匹配 JSON 节点并生成它的 JSON 文本输出的基本模板,然后您可以添加自己的专用模板来更改 SomeProperty/LowProperty1
值:
let $stuff :=
document {
object-node {
"SomeProperty": object-node {
"LowProperty1":"some string",
"LowProperty2":"some string",
"LowProperty3": array-node { "some string 1", "some string 2"}
}
}
}
let $target := xdmp:unpath("/EvenLowerProperty/LowestProperty1", (), $stuff)
return
xdmp:xslt-eval(
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
xdmp:dialect="1.0-ml">
<xsl:output method="text"/>
<xsl:variable name="lcurly" select="'{'"/>
<xsl:variable name="rcurly" select="'}'"/>
<xsl:template match="node()">
<xsl:apply-templates select="." mode="name"/>
<xsl:apply-templates select="." mode="value"/>
</xsl:template>
<xsl:template match="array-node()/node()">
<xsl:apply-templates select="." mode="value"/>
</xsl:template>
<xsl:template match="node()" mode="name">
<xsl:if test="string(node-name(.))!=''">"<xsl:value-of select="node-name(.)"/>": </xsl:if>
</xsl:template>
<xsl:template match="text()" mode="value">
<xsl:text>"</xsl:text><xsl:value-of select="."/><xsl:text>"</xsl:text>
<xsl:if test="following-sibling::node()">, </xsl:if>
</xsl:template>
<xsl:template match="number-node() | boolean-node()" mode="value">
<xsl:value-of select="."/>
<xsl:if test="following-sibling::node()">, </xsl:if>
</xsl:template>
<xsl:template match="object-node()" mode="value">
<xsl:value-of select="$lcurly"/>
<xsl:apply-templates select="node()"/>
<xsl:value-of select="$rcurly"/>
<xsl:if test="following-sibling::node()">,</xsl:if>
</xsl:template>
<xsl:template match="array-node()/object-node()" mode="value">
<xsl:value-of select="$lcurly"/>
<xsl:apply-templates select="node()"/>
<xsl:value-of select="$rcurly"/>
<xsl:if test="following-sibling::node()">,</xsl:if>
</xsl:template>
<xsl:template match="array-node()" mode="value">
<xsl:value-of select="'['"/>
<xsl:apply-templates select="node()"/>
<xsl:value-of select="']'"/>
<xsl:if test="following-sibling::node()">,</xsl:if>
</xsl:template>
<xsl:template match="null-node()" mode="value">
<xsl:value-of select="'null'"/>
<xsl:if test="following-sibling::node()">, </xsl:if>
</xsl:template>
<xsl:template match="SomeProperty/LowProperty1">
<xsl:apply-templates select="." mode="name"/>
<xsl:text>"bar"</xsl:text>
<xsl:if test="following-sibling::node()">, </xsl:if>
</xsl:template>
</xsl:stylesheet>,
$stuff
)
我受到@MadsHansen 发现 xdmp:dialect="1.0-ml"
选项的启发,为我的其他答案创建了一个更地道的版本。使用此 XSLT,您可以保持使用 MarkLogic JSON XPath 扩展(即:match="SomeProperty/LowProperty1"
)创建模板的能力。
这里的不同之处在于,不是在开始时批量转换为 json:object
XML,而是最初维护原生 JSON 对象,并且仅在转换期间转换为 json:object
转型。然后在最后,一切都被转换回原生。唯一的缺点是您需要在模板中构造新的 JSON 时使用 json:object
XML,或者将本机构造函数包装在 xdmp:from-json()
:
中
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" version="2.0"
xmlns:json="http://marklogic.com/xdmp/json"
xmlns:xdmp="http://marklogic.com/xdmp"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xdmp:dialect="1.0-ml">
<!-- XML -->
<xsl:template match="SomeProperty/LowProperty1">
<xsl:text>bar</xsl:text>
</xsl:template>
<!-- Native JSON syntax -->
<xsl:template match="SomeProperty/LowProperty2">
{xdmp:from-json(
object-node { "foo" : "bar" }
)}
</xsl:template>
<!-- Conversion handling -->
<xsl:template match="/">
<xsl:variable name="result" as="node()">
<xsl:apply-templates select="@*|node()"/>
</xsl:variable>
<xsl:choose>
<xsl:when test="namespace-uri-from-QName($result/node-name(.)) = 'http://marklogic.com/xdmp/json'">
<xsl:sequence select="xdmp:to-json(json:object($result))"/>
</xsl:when>
<xsl:otherwise>
<xsl:sequence select="$result"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- Identity templates below -->
<xsl:template name="json:value">
<xsl:variable name="result" as="node()">
<xsl:apply-templates select="."/>
</xsl:variable>
<json:value>
<xsl:if test=". instance of number-node()">
<xsl:attribute name="xsi:type">
<xsl:value-of select="xs:QName('xs:integer')"/>
</xsl:attribute>
</xsl:if>
<xsl:sequence select="$result"/>
</json:value>
</xsl:template>
<xsl:template match="object-node()">
<json:object>
<xsl:for-each select="node()">
<json:entry key="{{ name(.) }}">
<xsl:call-template name="json:value"/>
</json:entry>
</xsl:for-each>
</json:object>
</xsl:template>
<xsl:template match="array-node()">
<json:array>
<xsl:for-each select="node()">
<xsl:call-template name="json:value"/>
</xsl:for-each>
</json:array>
</xsl:template>
<xsl:template match="number-node()">
<xsl:value-of select="."/>
</xsl:template>
<xsl:template match="node()|@*" priority="-1">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
另请注意,原生 JSON 语法仅在与 xdmp:xslt-eval
一起使用时才有效 - 原生语法在 XQuery 中进行评估并在 XSLT 之前转换为 json:object
XML被评估。
我同意@wst 的观点,xsl:copy
有一些奇怪的地方。至少在 ML9 中,它确实似乎在进行复制,而不是递归到对象节点中。尚未使用 ML10 进行测试。
@mads-hansen 提到的 xdmp:dialect
在这里绝对有用,虽然复制时不需要将整个树转换为 XML 符号,然后再转换回 JSON到底。您还可以使用内部 json:object 类型,并在需要的地方进行浅层转换。
这展示了如何用适用于 JSON 的东西替换通常的身份转换:
xquery version "1.0-ml";
let $stuff :=
document {
object-node {
"SomeProperty": object-node {
"LowProperty1":"some string",
"LowProperty2":"some string",
"LowProperty3": array-node { "some string 1", "some string 2"}
}
}
}
let $target := xdmp:unpath("/EvenLowerProperty/LowestProperty1", (), $stuff)
return
xdmp:xslt-eval(
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0" xdmp:dialect="1.0-ml"
xmlns:json="http://marklogic.com/xdmp/json">
<xsl:template match="object-node()">
<xsl:variable name="this" select="json:object()"/>
<xsl:for-each select="node()">
<xsl:variable name="contents" as="item()*">
<xsl:apply-templates select="."/>
</xsl:variable>
<xsl:sequence select="map:put($this, name(.), $contents)"/>
</xsl:for-each>
<xsl:sequence select="xdmp:to-json($this)"/>
</xsl:template>
<xsl:template match="array-node()">
<xsl:variable name="contents" as="item()*">
<xsl:apply-templates select="node()"/>
</xsl:variable>
<xsl:sequence select="json:to-array($contents)"/>
</xsl:template>
<xsl:template match="text()">
<xsl:sequence select="."/>
</xsl:template>
<xsl:template match="SomeProperty/LowProperty1">
<xsl:text>foo</xsl:text>
</xsl:template>
</xsl:stylesheet>,
$stuff
)
HTH!
我有一些 JSON、一个 XPath 和一个值。我想用新值替换 XPath 指向的 JSON 属性 中的现有值。我想我可以用 XSLT 来做,但我不太擅长 XSLT。这将在 XQuery 模块中。
对于XML,我可以这样做:
let $content :=
document {
<class>
<student rollno = "393">
<firstname>Dinkar</firstname>
<lastname>Kad</lastname>
<nickname>Dinkar</nickname>
<marks>85</marks>
</student>
<student rollno = "493">
<firstname>Vaneet</firstname>
<lastname>Gupta</lastname>
<nickname>Vinni</nickname>
<marks>95</marks>
</student>
</class>
}
let $template :=
<xsl:stylesheet version = "1.0"
xmlns:xsl = "http://www.w3.org/1999/XSL/Transform">
<xsl:template match = "node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="student/marks">
<foo>bar</foo>
</xsl:template>
</xsl:stylesheet>
return
xdmp:xslt-eval($template, $content)
用 <foo>bar</foo>
元素正确替换了 class/student/marks
元素。
对于 JSON,我正在尝试这样做:
let $stuff :=
document {
object-node {
"SomeProperty": object-node {
"LowProperty1":"some string",
"LowProperty2":"some string",
"LowProperty3": array-node { "some string 1", "some string 2"}
}
}
}
let $target := xdmp:unpath("/EvenLowerProperty/LowestProperty1", (), $stuff)
return
xdmp:xslt-eval(
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0"
xmlns:json="http://marklogic.com/xdmp/json">
<xsl:template match="node()">
<xsl:copy>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="SomeProperty/LowProperty1">
{
map:entry("LowProperty1", "bar")
}
</xsl:template>
</xsl:stylesheet>,
$stuff
)
我想这样结束:
{
"SomeProperty": {
"LowProperty1":"bar",
"LowProperty2":"some string",
"LowProperty3": [ "some string 1", "some string 2" ]
}
}
相反,我得到了原件的副本。我已经尝试了一些变体,但我并没有更接近。我应该期望它起作用吗?
除非 MarkLogic 做了一些我不知道的事情来扩展标准 XSLT 语义,否则这是行不通的。 SomeProperty/LowProperty1
之类的匹配模式不能用于寻址 map/array 树的部分地址。你可以在这样的树中匹配东西,但它不是很有用,因为匹配不能是上下文敏感的:给定一个映射或数组,你无法找到它在哪里或如何到达那里。
您可能会发现阅读我的 XML 布拉格 2016 年关于使用 XSLT 3.0 转换 JSON 的论文很有用:http://www.saxonica.com/papers/xmlprague-2016mhk.pdf
使用 XSLT 模板匹配转换 XML 的标准方法不能很好地转换为 JSON,根本原因是 map/array 结构用于表示 JSON 没有 "node identity" 或向上导航(父指针)。在我论文中的示例中,我通常发现进行这种转换的最简单方法是将结构转换为 XML,转换 XML,然后再转换回来——尽管还有其他方法你可能会考虑。
我一直在尝试设计一种高阶扩展函数来简化此类任务。我认为我还没有理想的解决方案。
问题似乎是 MarkLogic 的 XSLT 处理器处理 JSON 扩展的程度与其 XQuery 处理器不同。 <xsl:copy>
似乎被 object-node()
短路,而不是仅复制上下文节点,其行为类似于 <xsl:copy-of>
,复制所有后代,这会阻止 LowProperty1
模板(以及任何其他模板)从执行。您可以通过向 LowProperty1
模板添加一个 <xsl:message>
来确认这一点,并看到该消息从未被记录。
据我所知,没有惯用的方法可以从 XSLT 中复制 JSON 节点。因此,另一种方法是在转换前后简单地转换 to/from json:object
- 当然这可以在 运行 XSLT 之前在 XQuery 中完成(这可能更可取)。
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" version="2.0"
xmlns:json="http://marklogic.com/xdmp/json"
xmlns:xdmp="http://marklogic.com/xdmp">
<xsl:template match="document-node()">
<xsl:variable name="jsonxml" as="element()">
<temp><xsl:sequence select="xdmp:from-json(.)"/></temp>
</xsl:variable>
<xsl:variable name="result" as="element(json:object)">
<xsl:apply-templates select="$jsonxml/json:object"/>
</xsl:variable>
<xsl:sequence select="xdmp:to-json(json:object($result))"/>
</xsl:template>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="json:entry[@key='LowProperty1']/json:value">
<xsl:copy>
<xsl:text>bar</xsl:text>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
如果设置 xdmp:dialect="1.0-ml"
,则可以对 JSON 节点类型使用模板匹配模式:object-node()
、array-node()
、number-node()
、boolean-node()
, null-node()
除了能够使用 XPath 和基于节点名称的匹配模式,例如 SomeProperty/LowProperty1
.
不幸的是,xsl:copy
执行深度复制,这使得转换变得困难,并且没有可用于这些 JSON 节点的 XSLT 节点构造器。
因此,将 JSON 转换为 XML、HTML 和文本非常容易,但是为了构建您想要的转换后的 JSON,您将要么像@wst 演示的那样转换 to/from json:object
,要么你可以稍微作弊,只生成 JSON.
使用一些匹配 JSON 节点并生成它的 JSON 文本输出的基本模板,然后您可以添加自己的专用模板来更改 SomeProperty/LowProperty1
值:
let $stuff :=
document {
object-node {
"SomeProperty": object-node {
"LowProperty1":"some string",
"LowProperty2":"some string",
"LowProperty3": array-node { "some string 1", "some string 2"}
}
}
}
let $target := xdmp:unpath("/EvenLowerProperty/LowestProperty1", (), $stuff)
return
xdmp:xslt-eval(
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
xdmp:dialect="1.0-ml">
<xsl:output method="text"/>
<xsl:variable name="lcurly" select="'{'"/>
<xsl:variable name="rcurly" select="'}'"/>
<xsl:template match="node()">
<xsl:apply-templates select="." mode="name"/>
<xsl:apply-templates select="." mode="value"/>
</xsl:template>
<xsl:template match="array-node()/node()">
<xsl:apply-templates select="." mode="value"/>
</xsl:template>
<xsl:template match="node()" mode="name">
<xsl:if test="string(node-name(.))!=''">"<xsl:value-of select="node-name(.)"/>": </xsl:if>
</xsl:template>
<xsl:template match="text()" mode="value">
<xsl:text>"</xsl:text><xsl:value-of select="."/><xsl:text>"</xsl:text>
<xsl:if test="following-sibling::node()">, </xsl:if>
</xsl:template>
<xsl:template match="number-node() | boolean-node()" mode="value">
<xsl:value-of select="."/>
<xsl:if test="following-sibling::node()">, </xsl:if>
</xsl:template>
<xsl:template match="object-node()" mode="value">
<xsl:value-of select="$lcurly"/>
<xsl:apply-templates select="node()"/>
<xsl:value-of select="$rcurly"/>
<xsl:if test="following-sibling::node()">,</xsl:if>
</xsl:template>
<xsl:template match="array-node()/object-node()" mode="value">
<xsl:value-of select="$lcurly"/>
<xsl:apply-templates select="node()"/>
<xsl:value-of select="$rcurly"/>
<xsl:if test="following-sibling::node()">,</xsl:if>
</xsl:template>
<xsl:template match="array-node()" mode="value">
<xsl:value-of select="'['"/>
<xsl:apply-templates select="node()"/>
<xsl:value-of select="']'"/>
<xsl:if test="following-sibling::node()">,</xsl:if>
</xsl:template>
<xsl:template match="null-node()" mode="value">
<xsl:value-of select="'null'"/>
<xsl:if test="following-sibling::node()">, </xsl:if>
</xsl:template>
<xsl:template match="SomeProperty/LowProperty1">
<xsl:apply-templates select="." mode="name"/>
<xsl:text>"bar"</xsl:text>
<xsl:if test="following-sibling::node()">, </xsl:if>
</xsl:template>
</xsl:stylesheet>,
$stuff
)
我受到@MadsHansen 发现 xdmp:dialect="1.0-ml"
选项的启发,为我的其他答案创建了一个更地道的版本。使用此 XSLT,您可以保持使用 MarkLogic JSON XPath 扩展(即:match="SomeProperty/LowProperty1"
)创建模板的能力。
这里的不同之处在于,不是在开始时批量转换为 json:object
XML,而是最初维护原生 JSON 对象,并且仅在转换期间转换为 json:object
转型。然后在最后,一切都被转换回原生。唯一的缺点是您需要在模板中构造新的 JSON 时使用 json:object
XML,或者将本机构造函数包装在 xdmp:from-json()
:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" version="2.0"
xmlns:json="http://marklogic.com/xdmp/json"
xmlns:xdmp="http://marklogic.com/xdmp"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xdmp:dialect="1.0-ml">
<!-- XML -->
<xsl:template match="SomeProperty/LowProperty1">
<xsl:text>bar</xsl:text>
</xsl:template>
<!-- Native JSON syntax -->
<xsl:template match="SomeProperty/LowProperty2">
{xdmp:from-json(
object-node { "foo" : "bar" }
)}
</xsl:template>
<!-- Conversion handling -->
<xsl:template match="/">
<xsl:variable name="result" as="node()">
<xsl:apply-templates select="@*|node()"/>
</xsl:variable>
<xsl:choose>
<xsl:when test="namespace-uri-from-QName($result/node-name(.)) = 'http://marklogic.com/xdmp/json'">
<xsl:sequence select="xdmp:to-json(json:object($result))"/>
</xsl:when>
<xsl:otherwise>
<xsl:sequence select="$result"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- Identity templates below -->
<xsl:template name="json:value">
<xsl:variable name="result" as="node()">
<xsl:apply-templates select="."/>
</xsl:variable>
<json:value>
<xsl:if test=". instance of number-node()">
<xsl:attribute name="xsi:type">
<xsl:value-of select="xs:QName('xs:integer')"/>
</xsl:attribute>
</xsl:if>
<xsl:sequence select="$result"/>
</json:value>
</xsl:template>
<xsl:template match="object-node()">
<json:object>
<xsl:for-each select="node()">
<json:entry key="{{ name(.) }}">
<xsl:call-template name="json:value"/>
</json:entry>
</xsl:for-each>
</json:object>
</xsl:template>
<xsl:template match="array-node()">
<json:array>
<xsl:for-each select="node()">
<xsl:call-template name="json:value"/>
</xsl:for-each>
</json:array>
</xsl:template>
<xsl:template match="number-node()">
<xsl:value-of select="."/>
</xsl:template>
<xsl:template match="node()|@*" priority="-1">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
另请注意,原生 JSON 语法仅在与 xdmp:xslt-eval
一起使用时才有效 - 原生语法在 XQuery 中进行评估并在 XSLT 之前转换为 json:object
XML被评估。
我同意@wst 的观点,xsl:copy
有一些奇怪的地方。至少在 ML9 中,它确实似乎在进行复制,而不是递归到对象节点中。尚未使用 ML10 进行测试。
@mads-hansen 提到的 xdmp:dialect
在这里绝对有用,虽然复制时不需要将整个树转换为 XML 符号,然后再转换回 JSON到底。您还可以使用内部 json:object 类型,并在需要的地方进行浅层转换。
这展示了如何用适用于 JSON 的东西替换通常的身份转换:
xquery version "1.0-ml";
let $stuff :=
document {
object-node {
"SomeProperty": object-node {
"LowProperty1":"some string",
"LowProperty2":"some string",
"LowProperty3": array-node { "some string 1", "some string 2"}
}
}
}
let $target := xdmp:unpath("/EvenLowerProperty/LowestProperty1", (), $stuff)
return
xdmp:xslt-eval(
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0" xdmp:dialect="1.0-ml"
xmlns:json="http://marklogic.com/xdmp/json">
<xsl:template match="object-node()">
<xsl:variable name="this" select="json:object()"/>
<xsl:for-each select="node()">
<xsl:variable name="contents" as="item()*">
<xsl:apply-templates select="."/>
</xsl:variable>
<xsl:sequence select="map:put($this, name(.), $contents)"/>
</xsl:for-each>
<xsl:sequence select="xdmp:to-json($this)"/>
</xsl:template>
<xsl:template match="array-node()">
<xsl:variable name="contents" as="item()*">
<xsl:apply-templates select="node()"/>
</xsl:variable>
<xsl:sequence select="json:to-array($contents)"/>
</xsl:template>
<xsl:template match="text()">
<xsl:sequence select="."/>
</xsl:template>
<xsl:template match="SomeProperty/LowProperty1">
<xsl:text>foo</xsl:text>
</xsl:template>
</xsl:stylesheet>,
$stuff
)
HTH!