在一个大的json文件中,我想从嵌套列表中删除一些元素,但保留文档的整体结构。
我的例子输入了它(但真实的一个足以要求流式传输)。
{
"keep_untouched": {
"keep_this": [
"this",
"list"
]
},
"filter_this":
[
{"keep" : "true"},
{
"keep": "true",
"extra": "keeper"
} ,
{
"keep": "false",
"extra": "non-keeper"
}
]
}
所需的输出只删除了'filter_this'块中的一个元素:
{
"keep_untouched": {
"keep_this": [
"this",
"list"
]
},
"filter_this":
[
{"keep" : "true"},
{
"keep": "true",
"extra": "keeper"
} ,
]
}
处理此类案例的标准方法似乎是使用'truncate_stream'重新组合流对象,然后以通常的jq方式过滤它们。具体来说,命令:
jq -nc --stream 'fromstream(1|truncate_stream(inputs))'
可以访问对象流:
{"keep_this":["this","list"]}
[{"keep":"true"},{"keep":"true","extra":"keeper"},
{"keep":"false","extra":"non-keeper"}]
此时可以轻松过滤所需的对象。但是,这会从父对象的上下文中删除结果,这不是我想要的。
观看流式结构:
[["keep_untouched","keep_this",0],"this"]
[["keep_untouched","keep_this",1],"list"]
[["keep_untouched","keep_this",1]]
[["keep_untouched","keep_this"]]
[["filter_this",0,"keep"],"true"]
[["filter_this",0,"keep"]]
[["filter_this",1,"keep"],"true"]
[["filter_this",1,"extra"],"keeper"]
[["filter_this",1,"extra"]]
[["filter_this",2,"keep"],"false"]
[["filter_this",2,"extra"],"non-keeper"]
[["filter_this",2,"extra"]]
[["filter_this",2]]
[["filter_this"]]
似乎我需要选择所有'filter_this'行,仅截断这些行(使用'truncate_stream'),将这些行重建为对象(使用'from_stream'),过滤它们,然后将对象转回流中数据格式(使用“tostream”)加入“保持未触动”的流,这些行仍然是流式格式。那时可以重新构建整个json。如果这是正确的方法 - 这似乎过度转移给我 - 我该怎么做?或者,还有更好的方法?
答案 0 :(得分:2)
如果您的输入文件包含一个非常大的JSON实体,该实体对于您的环境中的常规jq解析器来说太大,那么很可能您没有足够的内存来重构JSON文档。
有了这个警告,以下可能值得一试。关键的见解是可以使用reduce
完成重建。
为了清楚起见,以下使用了一堆临时文件:
TMP=/tmp/$$
jq -c --stream 'select(length==2)' input.json > $TMP.streamed
jq -c 'select(.[0][0] != "filter_this")' $TMP.streamed > $TMP.1
jq -c 'select(.[0][0] == "filter_this")' $TMP.streamed |
jq -nc 'reduce inputs as [$p,$x] (null; setpath($p;$x))
| .filter_this |= map(select(.keep=="true"))
| tostream
| select(length==2)' > $TMP.2
# Reconstruction
jq -n 'reduce inputs as [$p,$x] (null; setpath($p;$x))' $TMP.1 $TMP.2
{
"keep_untouched": {
"keep_this": [
"this",
"list"
]
},
"filter_this": [
{
"keep": "true"
},
{
"keep": "true",
"extra": "keeper"
}
]
}
答案 1 :(得分:1)
非常感谢@peak。我发现他的方法非常有用,但在性能方面却不切实际。然而,窃取@ peak的一些想法,我想出了以下内容:
提取'父'对象:
jq -c --stream 'select(length==2)' input.json |
jq -c 'select(.[0][0] != "filter_this")' |
jq -n 'reduce inputs as [$p,$x] (null; setpath($p;$x))' > $TMP.parent
提取'守护者' - 虽然这意味着两次读取文件(: - <):
jq -nc --stream '[fromstream(2|truncate_stream(inputs))
| select(type == "object" and .keep == "true")]
' input.json > $TMP.keepers
将筛选后的列表插入父对象。
jq -nc -s 'inputs as $items
| $items[0] as $parent
| $parent
| .filter_this |= $items[1]
' $TMP.parent $TMP.keepers > result.json
答案 2 :(得分:1)
以下是@ PeteC脚本的简化版本。它需要少量调用jq。
在这两种情况下,请注意使用" 2 | truncate_stream(_)"的jq的调用。需要更新版本的jq而不是1.5。
TMP=/tmp/$$
INPUT=input.json
# Extract all but .filter_this
< $INPUT jq -c --stream 'select(length==2 and .[0][0] != "filter_this")' |
jq -nc 'reduce inputs as [$p,$x] (null; setpath($p;$x))
' > $TMP.parent
# Need jq > 1.5
# Extract the 'keepers'
< $INPUT jq -n -c --stream '
[fromstream(2|truncate_stream(inputs))
| select(type == "object" and .keep == "true")]
' $INPUT > $TMP.keepers
# Insert the filtered list into the parent object:
jq -s '. as $in | .[0] | (.filter_this |= $in[1])
' $TMP.parent $TMP.keepers > result.json