XQuery 3.1 是为高级 JSON 编辑而设计的吗?

Is XQuery 3.1 designed for advanced JSON editing?

XQuery 3.1 引入了几个JSON functions。我想知道这些功能是否在设计时考虑了高级 JSON 编辑。

据我所知,这些函数仅适用于简单的 JSONs,例如...

let $json:={"a":1,"b":2} return map:put($json,"c",3)
{
  "a": 1,
  "b": 2,
  "c": 3
}

let $json:={"a":1,"b":2,"c":3} return map:remove($json,"c")
{
  "a": 1,
  "b": 2
}

JSON 变得有点复杂:

let $json:={"a":{"x":1,"y":2},"b":2} return map:put($json?a,"z",3)
{
  "x": 1,
  "y": 2,
  "z": 3
}

let $json:={"a":{"x":1,"y":2,"z":3},"b":2} return map:remove($json?a,"z")
{
  "x": 1,
  "y": 2
}

显然 map:put()map:remove() 完全按照您告诉他们的去做; select“a”对象并添加或删除属性。
但是,当我想编辑 JSON 文档时,我想编辑整个文档。据我所知,目前的实施是不可能的。或者是吗?至少 map:put($json,$json?a?z,3)map:remove($json,$json?a?z) 之类的东西不起作用。

为了删除“z”属性,我确实想出了一个自定义递归函数(它只适用于这个特定的用例)...

declare function local:remove($map,$key){
  if ($map instance of object()) then
    map:merge(
      map:keys($map)[.!=$key] ! map:entry(.,local:remove($map(.),$key))
    )
  else
    $map
};
let $json:={"a":{"x":1,"y":2,"z":3},"b":2} return
local:remove($json,"z")

...具有预期的输出...

{
  "a": {
    "x": 1,
    "y": 2
  },
  "b": 2
}

...但我无法创建自定义“添加”功能。

我认为高级 JSON 编辑可以通过一些非常高级的自定义功能来完成,但我非常希望看到像 map:put($json,$json?a?z,3) 这样的东西可以工作,或者一个额外的选项让 map:put() 输出整个 JSON 文档,例如 map:put($json?a?z,3, <extra-option> ).

或者...我不得不接受 XQuery 当然不是正确选择的观点。

你是对的,使用当前定义的 XQuery 3.1(实际上是 XSLT 3.0)执行我称之为地图深度更新的操作非常困难。定义具有清晰语义的语言结构并不容易。我试图将构造设计为 XSLT 扩展指令 - 参见 https://saxonica.com/documentation10/index.html#!extensions/instructions/deep-update - 但我认为它离完美解决方案还差得很远。

我想要同样的东西,所以编写了我自己的代理 XSLT 函数,tan:map-put()tan:map-remove(),它们进行深度映射替换和删除: https://github.com/Arithmeticus/XML-Pantry/tree/master/maps-and-arrays

这些可以通过 xsl:includexsl:import 合并到 XSLT 工作流中,或者通过 fn:transform() 合并到 XQuery 工作流中。其他一些功能也可能有用。如果这些功能不能完全满足您的要求,它们可能会催化您自己的变化。

在 XQuery 3.1 中,您应该为这些事情编写一个递归函数。您可以将所有功能放在模块文件中,然后在需要时加载模块...

除此之外,Xidel 在 JSONiq 和 XPath 3.1 之前还有一个对象编辑扩展。对于一个全局可变变量(没有 let),你可以这样写:

$json:={"a":{"x":1,"y":2,"z":3},"b":2},
(($json).a).z := 4   


$json:={"a":{"x":1,"y":2,"z":3},"b":2},
$json("a")("z") := 4

来自@ChristianGrün:

If updates are required, we tend to convert JSON data to XML.

我是 user and last week (with a little help from @BeniBela) I've had a look at whether this could be done with json-to-xml(), Xidel's own x-replace-nodes()xml-to-json()。答案是肯定的。谢谢提示。

这里有 1 个示例供参考和任何感兴趣的人参考。

要将 {"x":{"a":1,"b":2,"c":3},"y":2} 中的键“c”更改为“d”:

$ xidel -s '{"x":{"a":1,"b":2,"c":3},"y":2}' -e '
  xml-to-json(
    x:replace-nodes(
      json-to-xml(
        serialize($json,{"method":"json"})
      )//fn:map[@key="x"]/fn:number[@key="c"]/@key,
      attribute key {"d"}
    )
  )
'
{"x":{"a":1,"b":2,"d":3},"y":2}

Xidel online tester.