如何通过node.js中的JSONStream模块解析一个大的、换行符分隔的JSON文件?

Posted

技术标签:

【中文标题】如何通过node.js中的JSONStream模块解析一个大的、换行符分隔的JSON文件?【英文标题】:how to parse a large, Newline-delimited JSON file by JSONStream module in node.js? 【发布时间】:2013-02-13 19:41:26 【问题描述】:

我有一个大的 json 文件,它是换行符分隔的 JSON,其中多个标准 JSON 对象由额外的换行符分隔,例如

'name':'1','age':5
'name':'2','age':3
'name':'3','age':6

我现在在node.js中使用JSONStream来解析一个大的json文件,我之所以使用JSONStream是因为它是基于流的。

但是,示例中的两种解析语法都无法帮助我解析这个 json 文件,每行中都有单独的 JSON

var parser = JSONStream.parse(**['rows', true]**);
var parser = JSONStream.parse([**/./**]);

谁能帮帮我

【问题讨论】:

逐行读取文件并分别解析。 【参考方案1】:

当文件足够小以适合内存时,这是另一种解决方案。它一次性读取整个文件,通过在换行符处拆分(删除末尾的空白行)将其转换为数组,然后解析每一行。

import fs from "fs";

const parsed = fs
  .readFileSync(`data.jsonl`, `utf8`)
  .split(`\n`)
  .slice(0, -1)
  .map(JSON.parse)

【讨论】:

请在代码中添加更多解释,帮助我们理解更多 完成,如果您需要更多信息,请告诉我。【参考方案2】:

我创建了一个包@jsonlines/core,它将jsonlines解析为对象流。

你可以试试下面的代码:

npm install @jsonlines/core
const fs = require("fs");
const  parse  = require("@jsonlines/core");

// create a duplex stream which parse input as lines of json
const parseStream = parse();

// read from the file and pipe into the parseStream
fs.createReadStream(yourLargeJsonLinesFilePath).pipe(parseStream);

// consume the parsed objects by listening to data event
parseStream.on("data", (value) => 
  console.log(value);
);

请注意,parseStream 是标准节点双工流。 所以你也可以使用for await ... of或者其他方式消费。

【讨论】:

【参考方案3】:

如果您的文件不够大,这是一个简单但不高效的解决方案:

const fs = require('fs');

let rawdata = fs.readFileSync('fileName.json');

let convertedData = String(rawdata)
    .replace(/\n/gi, ',')
    .slice(0, -1);

let JsonData= JSON.parse(`[$convertedData]`); 

【讨论】:

【参考方案4】:

JSONstream 旨在解析单个巨大的 JSON 对象,而不是很多 JSON 对象。您想在换行符处拆分流,然后将它们解析为 JSON。

NPM 包split 声称可以进行这种拆分,甚至还有一个feature to parse the JSON lines 供您使用。

【讨论】:

我发现 split 包非常有用,事实上,使用 split+JSON.parse() 优于 JSONStream。【参考方案5】:

警告:由于写了这个答案,JSONStream 库的作者removed the emit root event functionality,显然是为了修复内存泄漏。 该库的未来用户,如果您需要发出根功能,可以使用 0.x.x 版本。

以下是未经修改的原始答案

来自readme:

JSONStream.parse(路径)

path 应该是属性名称、正则表达式、布尔值和/或函数的数组。任何与路径匹配的对象都将作为'data' 发出。

当接收到所有数据时会发出'root' 事件。 'root' 事件传递根对象和匹配对象的计数。

在您的情况下,由于您想要取回 JSON 对象而不是特定属性,因此您将使用 'root' 事件并且您不需要指定路径。

您的代码可能如下所示:

var fs = require('fs'),
    JSONStream = require('JSONStream');

var stream = fs.createReadStream('data.json', encoding: 'utf8'),
    parser = JSONStream.parse();

stream.pipe(parser);

parser.on('root', function (obj) 
  console.log(obj); // whatever you will do with each JSON object
);

【讨论】:

注意 var 行末尾的错字 - ';'应该是','。 @frangio 如果需要读取大文件,请根据 OPs 问题澄清用法,但直接将 Objectified 流传递给管道中的下一个 Transform。例如。我想要 stream.pipe(parser).pipe(MyNextTransform) ,其中 MyNextTransform 可以处理它在 _transform() 方法中作为参数接收的对象。换句话说,希望 parser.on('root') 的输出被委托到另一个可读流上以进行进一步的管道处理。 没关系,我解决了 :) 只需返回 stream.pipe(parser),链中的下一个转换将自动给出 parser.on('root') 调用的结果.

以上是关于如何通过node.js中的JSONStream模块解析一个大的、换行符分隔的JSON文件?的主要内容,如果未能解决你的问题,请参考以下文章

使用 JSONPath 和 JSONStream 解析 json 流

如何在 Node.js 中使用流对大型嵌套对象进行 JSON 字符串化?

我如何连接到 Node.js 中的星号

如何将 Node.js 中的 net 模块与 browserify 一起使用?

node.js 中的 module.parent 有啥用?如何引用 require()ing 模块?

如何在 node.js 中的“require”之后删除模块?