我有一些相当大的json文件(~500mb - 4gb压缩),我无法加载到内存中进行操作。所以我在jq。
中使用了--stream
选项
例如我的json可能看起来像这样 - 只是更大:
[{
"id": "0001",
"type": "donut",
"name": "Cake",
"ppu": 0.55,
"batters": {
"batter": [{
"id": "1001",
"type": "Regular"
}, {
"id": "1002",
"type": "Chocolate"
}, {
"id": "1003",
"type": "Blueberry"
}, {
"id": "1004",
"type": "Devil's Food"
}]
},
"topping": [{
"id": "5001",
"type": "None"
}, {
"id": "5002",
"type": "Glazed"
}, {
"id": "5005",
"type": "Sugar"
}, {
"id": "5007",
"type": "Powdered Sugar"
}, {
"id": "5006",
"type": "Chocolate with Sprinkles"
}, {
"id": "5003",
"type": "Chocolate"
}, {
"id": "5004",
"type": "Maple"
}]
}, {
"id": "0002",
"type": "donut",
"name": "Raised",
"ppu": 0.55,
"batters": {
"batter": [{
"id": "1001",
"type": "Regular"
}]
},
"topping": [{
"id": "5001",
"type": "None"
}, {
"id": "5002",
"type": "Glazed"
}, {
"id": "5005",
"type": "Sugar"
}, {
"id": "5003",
"type": "Chocolate"
}, {
"id": "5004",
"type": "Maple"
}]
}, {
"id": "0003",
"type": "donut",
"name": "Old Fashioned",
"ppu": 0.55,
"batters": {
"batter": [{
"id": "1001",
"type": "Regular"
}, {
"id": "1002",
"type": "Chocolate"
}]
},
"topping": [{
"id": "5001",
"type": "None"
}, {
"id": "5002",
"type": "Glazed"
}, {
"id": "5003",
"type": "Chocolate"
}, {
"id": "5004",
"type": "Maple"
}]
}]
如果这是我可以在内存中保存的文件类型,并且我想选择只有击球员类型"巧克力"的对象,我可以使用:
cat sample.json | jq '.[] | select(.batters.batter[].type == "Chocolate")'
我只会使用ids "0001"
和"0003"
但是通过流式传输,我知道它与众不同。
我正在阅读关于流媒体here和here的jq文档,但我仍然感到很困惑,因为这些示例并未真正展示json的真实世界问题。
即,甚至可以在流式传输路径并识别显着事件后选择整个对象,或者在这种情况下是与某个字符串匹配的属性值?
我知道我可以使用:
cat sample.json | jq --stream 'select(.[0][1] == "batters" and .[0][2] == "batter" and .[0][4] == "type") | .[1]'
给我所有的击球手类型。但有没有办法说:"如果它是巧克力,抓住这个叶子是#34的一部分?
答案 0 :(得分:1)
命令:
$ jq -cn --stream 'fromstream(1|truncate_stream(inputs))' array_of_objects.json |
jq 'select(.batters.batter[].type == "Chocolate") | .id'
输出:
"0001"
"0003"
第一次调用jq会将对象数组转换为对象流。第二种是基于您的调用,可以根据您的需求进行定制。
当然,两个调用可以(也可能应该)合并为一个,但您可能希望使用第一个调用将大文件保存为包含对象流的文件。
顺便说一下,使用以下select
:
select( any(.batters.batter[]; .type == "Chocolate") )
答案 1 :(得分:0)
这是另一种方法。从流式过滤器filter1.jq
开始,它提取您需要处理的记录号和最小属性集。 E.g。
select(length==2)
| . as [$p, $v]
| {r:$p[0]}
| if $p[1] == "id" then .id = $v
elif $p[1] == "batters" and $p[-1] == "type" then .type = $v
else empty
end
使用
运行此功能jq -M -c --stream -f filter1.jq bigdata.json
产生类似
的值{"r":0,"id":"0001"}
{"r":0,"type":"Regular"}
{"r":0,"type":"Chocolate"}
{"r":0,"type":"Blueberry"}
{"r":0,"type":"Devil's Food"}
{"r":1,"id":"0002"}
{"r":1,"type":"Regular"}
{"r":2,"id":"0003"}
{"r":2,"type":"Regular"}
{"r":2,"type":"Chocolate"}
现在将其传输到第二个过滤器filter2.jq
,它会对每个记录的那些属性执行所需的处理
foreach .[] as $i (
{c: null, r:null, id:null, type:null}
; .c = $i
| if .r != .c.r then .id=null | .type=null | .r=.c.r else . end # control break
| .id = if .c.id == null then .id else .c.id end
| .type = if .c.type == null then .type else .c.type end
; if ([.id, .type] | contains([null])) then empty else . end
)
| select(.type == "Chocolate").id
使用类似
的命令jq -M -c --stream -f filter1.jq bigdata.json | jq -M -s -r -f filter2.jq
生产
0001
0003
filter1.jq
和filter2.jq
比您针对此特定问题所需的更多,但它们可以轻松推广。