如何递归合并继承的json数组元素? [英] How to recursively merge inherited json array elements?

查看:23
本文介绍了如何递归合并继承的json数组元素?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个名为CMakePresets.json的json文件,它是cmake-preset文件:

{
  "configurePresets": [
    {
      "name": "default",
      "hidden": true,
      "generator": "Ninja",
      "binaryDir": "${sourceDir}/_build/${presetName}",
      "cacheVariables": {
        "YIO_DEV": "1",
        "BUILD_TESTING": "1"
      }
    },
    {
      "name": "debug",
      "inherits": "default",
      "cacheVariables": {
        "CMAKE_BUILD_TYPE": "Debug"
      }
    },
    {
      "name": "release",
      "inherits": "default",
      "binaryDir": "${sourceDir}/_build/Debug",
      "cacheVariables": {
        "CMAKE_BUILD_TYPE": "Release"
      }
    },
    {
      "name": "arm",
      "inherits": "debug",
      "cacheVariables": {
        "CMAKE_TOOLCHAIN_FILE": "${sourceDir}/cmake/Toolchain/arm-none-eabi-gcc.cmake"
      }
    }
  ]
}
我希望与*元素递归合并,这些元素为特定条目name继承自身。我有一个名称为arm的节点,并希望得到具有解析继承的结果json对象。父元素的名称存储在每个元素的.inherits中。arm继承的debug继承的default

我可以在Remove a key:value from an JSON object using jqthis answer的帮助下编写我认为可以工作的bash外壳循环:

input=arm
# extract one element
g() { jq --arg name "$1" '.configurePresets[] | select(.name == $name)' CMakePresets.json; };
# get arm element
acc=$(g "$input");
# If .inherits field exists
while i=$(<<<"$acc" jq -r .inherits) && [[ -n "$i" && "$i" != "null" ]]; do
   # remove it from input
   a=$(<<<"$acc" jq 'del(.inherits)');
   # get parent element
   b=$(g "$i");
   # merge parent with current
   acc=$(printf "%s
" "$b" "$a" | jq -s 'reduce .[] as $item ({}; . * $item)');
done;
echo "$acc"

输出,我认为这是arm的预期输出:

{
  "name": "arm",
  "hidden": true,
  "generator": "Ninja",
  "binaryDir": "${sourceDir}/_build/${presetName}",
  "cacheVariables": {
    "YIO_DEV": "1",
    "BUILD_TESTING": "1",
    "CMAKE_BUILD_TYPE": "Debug",
    "CMAKE_TOOLCHAIN_FILE": "${sourceDir}/cmake/Toolchain/arm-none-eabi-gcc.cmake"
  }
}

但我想用jq来写。我试过了,jq语言对我来说不是很直观。例如,我可以为两个人(即,可数)元素:

< CMakePresets.json jq --arg name "arm" '
   def g(n): .configurePresets[] | select(.name == n);
   g($name) * (g($name) | .inherits) as $name2 | g($name2)
'
但我不知道怎么做reduce .[] as $item ({}; . * $item)$item真的是g($name),这取决于最后的g($name) | .inherits。我试着阅读jq manual并学习变量和循环,但jq有非常不同的语法。我尝试使用while,但这只是一个语法错误,我不理解,也不知道如何修复。我猜whileuntil可能不在这里,因为它们在前面的循环输出上操作,而元素总是从根开始。

$ < CMakePresets.json jq --arg name "arm" 'def g(n): .configurePresets[] | select(.name == n);
while(g($name) | .inherits as $name; g($name))   
'
jq: error: syntax error, unexpected ';', expecting '|' (Unix shell quoting issues?) at <top-level>, line 2:
while(g($name) | .inherits as $name; g($name))                                      
jq: 1 compile error

如何用jq语言编写这样的循环?

推荐答案

假设继承层次结构不包含循环,如本例所示,我们可以将问题分解为以下几个部分:

# Use an inner function of arity 0 to take advantage of jq's TCO
def inherits_from($dict):
  def from:
    if .name == "default" then .
    else $dict[.inherits] as $next
    | ., ($next | from)
    end;
  from;

def chain($start):
  INDEX(.configurePresets[]; .name) as $dict
  | $dict[$start] | inherits_from($dict);

reduce chain("arm") as $x (null;
  ($x.cacheVariables + .cacheVariables) as $cv
  | $x + .
  | .cacheVariables = $cv)
| del(.inherits)

这将有效地产生所需的输出。

上述解决方案公式的一个优点是,可以轻松地对其进行修改以处理循环依赖。

使用recurse/1

inherits_from/1也可以使用内置函数recurse/1定义:

def inherits_from($dict):
  recurse( select(.name != "default") | $dict[.inherits]) ;

或者更有趣的是:

def inherits_from($dict):
  recurse( select(.inherits) | $dict[.inherits]) ;

使用*

使用*组合对象的开销很高,因为它的递归语义通常不是必需的,就是不需要的。然而, 如果使用*组合对象在这里是可以接受的,则上述内容可以简化为:

def inherits_from($dict):
  recurse( select(.inherits) | $dict[.inherits]) ;

INDEX(.configurePresets[]; .name) as $dict
| $dict["arm"] 
| reduce inherits_from($dict) as $x ({};  $x * .)
| del(.inherits)

这篇关于如何递归合并继承的json数组元素?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆