我必须解析具有以下格式的大型(100 MB以上)JSON文件:
{
"metadata": {
"account_id": 1234
// etc.
},
"transactions": [
{
"transaction_id": 1234,
"amount": 2
},
// etc. for (potentially) 1000's of lines
]
}
此解析的输出是一个JSON数组,其中account_id
附加到每个transactions
:
[
{
"account_id": 1234,
"transaction_id": 1234,
"amount": 2
},
// etc.
]
我正在使用stream-json库来避免将整个文件同时加载到内存中。 stream-json允许我pick个单个属性,然后一次流化一个属性,具体取决于它们是array还是object
我还试图通过将JSON文件的读取传递到两个单独的流which is possible in nodejs中来避免两次解析JSON。
我正在使用Transform流来生成输出,并在存储account_id
的Transform流对象上设置了一个属性。
下面的伪代码(具有明显的竞争条件):
const { parser } = require('stream-json');
const { pick } = require('stream-json/filters/Pick');
const { streamArray } = require('stream-json/streamers/StreamArray');
const { streamObject } = require('stream-json/streamers/StreamObject');
const Chain = require('stream-chain');
const { Transform } = require('stream');
let createOutputObject = new Transform({
writableObjectMode:true,
readableObjectMode:true,
transform(chunk, enc, next) => {
if (createOuptutObject.account_id !== null) {
// generate the output object
} else {
// Somehow store the chunk until we get the account_id...
}
}
});
createOutputObject.account_id = null;
let jsonRead = fs.createReadStream('myJSON.json');
let metadataPipline = new Chain([
jsonRead,
parser(),
pick({filter: 'metadata'}),
streamObject(),
]);
metadataPipeline.on('data', data => {
if (data.key === 'account_id') {
createOutputObject.account_id = data.value;
}
});
let generatorPipeline = new Chain([
jsonRead, // Note same Readable stream as above
parser(),
pick({filter: 'tracks'}),
streamArray(),
createOutputObject,
transformToJSONArray(),
fs.createWriteStream('myOutput.json')
]);
要解决这种竞争情况(即在设置account_id
之前转换为JSON数组),我尝试过:
createOutputObject.cork()
保留数据,直到设置account_id
。
transformToJSONArray()
。chunk
保留在createOutputObject
中的数组中,直到设置了account_id
。
chunk
之后如何重新添加存储的account_id
。setImmediate()
和process.nextTick()
呼叫createOutputObject.transform
,希望设置account_id
。
我已经考虑过使用流json的streamValues
函数,这将允许我执行pick
和metadata
的{{1}}。但是documentation使我相信transactions
的 all 都将被加载到内存中,这是我要避免的事情:
作为每个流媒体,它假定单个对象可以容纳在内存中,但是应该流传输整个文件或任何其他源。
还有其他可以解决这种竞争状况的东西吗?无论如何,我可以避免两次解析此大型JSON流吗?