我具有以下格式的JSON解压缩后,大约有96gzip
JSON,这是公开的350 GB JSON文件
{
"structe": {},
"beta": {},
"flow": {
"1023": {
"0101": {
"-LEjllNyHqdHYGntO6vu": {
"status": "1",
"t": 1528736191996
},
"-LEjllcXKaVOQu3BDpHF": {
"status": "1",
"t": 1528736192996
}
},
"0102": {
"-LEjllNyHqdHYGntO6vu": {
"status": "1",
"t": 1528736191996
},
"-LEjllcXKaVOQu3BDpHF": {
"status": "1",
"t": 1528736192996
}
}
},
"1024": {
"0103": {
"-LEjllNyHqdHYGntO6vu": {
"lat": 51.128676733981,
"lng": -113.9318991267252,
"status": "1",
"t": 1528736191996
},
"-LEjllcXKaVOQu3BDpHF": {
"lat": 51.128676733981,
"lng": -113.9318991267252,
"status": "1",
"t": 1528736192996
}
}
}
}
}
我无法将其加载到RAM中,现在我想流传输此文件并将路径flow->1023(let id1)->0101(let id2)
拉到新的id1_id2.json
文件中。任何人都想如何快速地做到这一点。我正在寻找的输出就像文件名= 1023_0101.json
{
"-LEjllNyHqdHYGntO6vu": {
"status": "1",
"t": 1528736191996
},
"-LEjllcXKaVOQu3BDpHF": {
"status": "1",
"t": 1528736192996
}
}
我首先想到的是将文件视为流,并逐行读取。已经有一些库将json文件视为流。例如,您可以从ijson库中检出代码段:
对于JSON,如:
{
"earth": {
"europe": [
{"name": "Paris", "type": "city", "info": { ... }},
{"name": "Thames", "type": "river", "info": { ... }},
// ...
],
"america": [
{"name": "Texas", "type": "state", "info": { ... }},
// ...
]
}
}
治疗看起来像:
import ijson
parser = ijson.parse(urlopen('http://.../'))
stream.write('<geo>')
for prefix, event, value in parser:
if (prefix, event) == ('earth', 'map_key'):
stream.write('<%s>' % value)
continent = value
elif prefix.endswith('.name'):
stream.write('<object name="%s"/>' % value)
elif (prefix, event) == ('earth.%s' % continent, 'end_map'):
stream.write('</%s>' % continent)
stream.write('</geo>')
您可以将jq
与--stream
选项一起使用,并设置为jq - I/O (Streaming),以流方式读取文本,从而允许程序立即开始处理大型JSON文本,而不是在解析完成之后(将整个文件存储在RAM中) 。
假设您输入的ID字符串存储在shell变量上下文中
id1=1023; id2=0101
将巨大的gzip
的输出传递到以下过滤器中
jq --arg v1 "$id1" --arg v2 "$id2" --stream 'fromstream(inputs)| objects | .flow[$v1][$v2]' > "$id1"_"$id2".json
((或)如果无法预取id名称,并且您需要在运行时获取它们,则直接将其名称用作
jq --stream 'fromstream(inputs)| objects | .flow."1023"."0101"'
这里是使用GNU awk及其gawk-json扩展名的一个:
$ cat program.awk
@load "json" # load extension
{
lines=lines $0 # gather json lines
if(json_fromJSON(lines,datain)==1) { # once complete object
for(i in datain["flow"]["1023"]["0101"]) # get wanted pieces
for(j in datain["flow"]["1023"]["0101"][i])
dataout[i][j]=datain["flow"]["1023"]["0101"][i][j] # into another array
print json_toJSON(dataout) # which gets output
lines="" # rinse for next repeat
delete(dataout)
}
}
运行它,将每个gz循环到stdout中,以供GNU awk处理:
$ for gz in *.gz ; do gunzip -c $gz ; done | gawk -f program.awk
输出几个gzs的数据:
{"-LEjllcXKaVOQu3BDpHF":{"status":"1","t":1528736192996},"-LEjllNyHqdHYGntO6vu":{"status":"1","t":1528736191996}}
{"-LEjllcXKaVOQu3BDpHF":{"status":"1","t":1528736192996},"-LEjllNyHqdHYGntO6vu":{"status":"1","t":1528736191996}}
对于sissy漂亮打印,输出到jq '.'
以上
这是一种使用jq的流解析器生成由$ id1,$ id2和相应的感兴趣值组成的流的解决方案;然后可以将该流传输到另一个工具中(例如,如果方便的话,请使用awk)以生成所需的文件。
以下,我们使用jq食谱中的atomize
:
def atomize(s):
fromstream(foreach s as $in ( {previous:null, emit: null};
if ($in | length == 2) and ($in|.[0][0]) != .previous and .previous != null
then {emit: [[.previous]], previous: $in|.[0][0]}
else { previous: ($in|.[0][0]), emit: null}
end;
(.emit // empty), $in) ) ;
然后简单地使用(用--stream -n -c调用的主要jq程序:
atomize(inputs)
| select(type == "object" and .flow)
| .flow
| keys_unsorted[] as $id1
| (.[$id1] | keys_unsorted[]) as $id2
| $id1, $id2, .[$id1][$id2]
因此,对于每个gzip文件$ gz,管道如下所示:
gunzip -c $ gz |jq -nc --stream -f program.jq |awk ....
有关使用awk产生所需结果的示例,请参见jq, split a huge json of array and save into file named with a value
jq的流解析器避免以牺牲速度为代价使用RAM,因此通常仅在没有其他选择时才使用--stream选项。从问题的描述来看,您似乎可以使用jq的常规解析器处理某些压缩文件,因此您可能希望快速处理这些文件,而对那些太大的文件保留“ atomize”方法。