如何使用jq过滤JSON对象以仅选择特定的键/值?

时间:2019-07-03 23:17:27

标签: jq

我正在尝试验证versions.json文件中的所有版本,并获取仅包含无效版本的json作为输出。

这是一个示例文件:

{
  "slamx": "16.4.0 ",
  "sdbe": null,
  "mimir": null,
  "thoth": null,
  "quasar": null,
  "connectors": {
    "s3": "16.0.17",
    "azure": "6.0.17",
    "url": "8.0.2",
    "mongo": "7.0.15"
  }
}

我可以使用以下jq脚本行来完成我想做的事情:

delpaths([paths(type == "string" and contains(" ") or type == "object" | not)]) 
| delpaths([paths(type == "object" and (to_entries | length == 0))])

并在这样的shell上使用它:

BAD_VERSIONS=$(jq 'delpaths([paths(type == "string" and contains(" ") or type == "object" | not)]) | delpaths([paths(type == "object" and (to_entries | length == 0))])' versions.json)

if [[ $BAD_VERSIONS != "{}" ]]; then
  echo >&2 $'Bad versions detected in versions.json:\n'"$BAD_VERSIONS"
  exit 1
fi

并将其作为输出:

Bad versions detected in versions.json:
{
  "slamx": "16.4.0 "
}

但是,这是进行过滤的一种非常复杂的方法。我需要创建一个我不想要的东西列表并将其删除,而不是只走路径树并说“保留,保留那个”,两次

考虑到所有的路径处理内置函数和递归处理,我不禁感到必须有一种更好的方法,类似于select,但是可以跨对象进行递归工作,但是我能做的最好的是:

. as $input | 
[path(recurse(.[]?)|select(strings|contains("16")))] as $paths | 
reduce $paths[] as $x ({}; . | setpath($x; ($input | getpath($x))))

我不喜欢这样做有两个原因。首先,我要创建一个新对象,而不是“编辑”旧对象。其次,它充满了变量,这指出了严重的流反转问题,并增加了复杂性。

有什么想法吗?

2 个答案:

答案 0 :(得分:0)

  1. 对于对象,测试location.pathname.startswith....可以缩写为to_entries|length == 0

  2. 如果我正确理解了目标,则可以使用length==0,也许可以遵循以下几行:

..

路径

如果需要路径,请考虑:

..
| objects
| with_entries(
    select(( .value|type == "string" and contains(" ")) or (.value|type == "object" and length==0)) )
| select(length>0)

修改输入内容以使s3尾随空白,上面的代码将产生:

([], paths) as $p
| getpath($p)
| objects
| with_entries(
        select(( .value|type == "string" and contains(" ")) or (.value|type == "object" and length==0)) )
| select(length>0) as $x
| {} | setpath($p; $x)

答案 1 :(得分:0)

感谢@jhnc's comment,我找到了解决方案。诀窍是使用流,这使嵌套变得无关紧要-我可以仅基于值应用过滤器,并且将根据键路径重新组合对象。

但是,我尝试的第一件事没有用。这个:

jq -c 'tostream|select(.[-1] | type=="string" and contains(" "))' versions.json

返回[["slamx"],"16.4.0 "],这就是我要搜索的内容。但是,我无法将其折回一个对象。为此,流必须具有“关闭对象”标记-仅具有一个元素的数组,该元素与要关闭的对象的最后一个键相对应。所以我将其更改为:

jq -c  'tostream|select((.[-1] | type=="string" and contains(" ")) or length==1)' versions.json

将其分解,.[-1]选择数组的最后一个元素,将其作为值。接下来,type=="string" and contains(" ")将选择所有值为字符串且包含空格的值。选择的最后部分length==1保留所有“结束”标记。有趣的是,即使结束标记与最后一个键都不对应,它也可以工作,因此可能很脆弱。

完成后,我可以将其分流:

jq -c  'fromstream(tostream|select((.[-1] | type=="string" and contains(" ")) or length==1))' versions.json

jq表达式如下:

fromstream(
    tostream |
    select(
        (
            .[-1] | 
            type=="string" and contains(" ")
        ) or 
        length==1
    )
)
相关问题