网易首页 > 网易号 > 正文 申请入驻

你知道如何在 Node.js 中流式处理器 JSON 文件吗

0
分享至

解决一个问题不只要搜寻最终的答案,寻找答案的过程同样也是重要的,善于思考与总结总归是好的。

本文介绍一个概念 SAX 的设计模式,这个概念虽然不是来源于 Node.js,但它解决问题的一些思想当我们在使用 Node.js 或一些其它的编程语言中遇到类似问题时也会受到一些启发,本文后面会介绍如何流式处理一个大 JSON 文件,下面先给出了两个问题,可以先思考下如果是你会怎么做?

场景描述

问题一:假设现在有一个场景,有一个大的 JSON 文件,需要读取每一条数据经过处理之后输出到一个文件或生成报表数据,怎么能够流式的每次读取一条记录?

[ {"id": 1}, {"id": 2}, ...]

问题二:同样一个大的 JSON 文件,我只读取其中的某一块数据,想只取 list 这个对象数组怎么办?

{ "list": [], "otherList": []}

在 Node.js 中我们可以基于以下几种方式读取数据,也是通常首先能够想到的:

  • fs.readFile():这个是一次性读取数据到内存,数据量大了都占用到内存也不是好办法,很容易造成内存溢出。
  • fs.createReadStream():创建一个可读流,能解决避免大量数据占用内存的问题,这是一个系统提供的基础 API 读取到的是一个个的数据块,因为我们的 JSON 对象是结构化的,也不能直接解决上面提的两个问题。
  • 还有一个 require() 也可以加载 JSON 文件,但是稍微熟悉点 Node.js CommonJS 规范的应该知道 require 加载之后是会缓存的,会一直占用在服务的内存里。

了解下什么是 SAX

SAX 是 Simple API for XML 的简称,目前没有一个标准的 SAX 参考标准,最早是在 Java 编程语言里被实现和流行开的,以 Java 对 SAX 的实现后来也被认为是一种规范。其它语言的实现也是遵循着该规则,尽管每门语言实现都有区别,但是这里有一个重要的概念 “事件驱动” 是相同的。

实现了 SAX 的解析器拥有事件驱动那样的 API,像 Stream 的方式来工作,边读取边解析,用户可以定义回调函数获取数据,无论 XML 内容多大,内存占用始终都会很小。

这对我们本节有什么帮助?我们读取解析一个大 JSON 文件的时候,也不能把所有数据都加载到内存里,我们也需要一个类似 SAX 这样的工具帮助我们实现。

基于 SAX 的流式 JSON 解析器

这是一个流式 JSON 解析器 https://github1s.com/creationix/jsonparse 周下载量在 600 多万,但是这个源码看起来很难梳理。如果是学习,推荐一个基于 SAX 的更简单版本 https://gist.github.com/creationix/1821394 感兴趣的可以看看。

JSON 是有自己的标准的,有规定的数据类型、格式。这个 JSON 解析器也是在解析到特定的格式或类型后触发相应的事件,我们在使用时也要注册相应的回调函数。

下面示例,创建一个可读流对象,在流的 data 事件里注册 SaxParser 实例对象的 parse 方法,也就是将读取到的原始数据(默认是 Buffer 类型)传递到 parse() 函数做解析,当解析到数据之后触发相应事件。

对应的 Node.js 代码如下:

const SaxParser = require('./jsonparse').SaxParser;const p = new SaxParser({ onNull: function () { console.log("onNull") }, onBoolean: function (value) { console.log("onBoolean", value) }, onNumber: function (value) { console.log("onNumber", value) }, onString: function (value) { console.log("onString", value) }, onStartObject: function () { console.log("onStartObject") }, onColon: function () { console.log("onColon") }, onComma: function () { console.log("onComma") }, onEndObject: function () { console.log("onEndObject") }, onStartArray: function () { console.log("onEndObject") }, onEndArray: function () { console.log("onEndArray") }});const stream = require('fs').createReadStream("./example.json");const parse = p.parse.bind(p);stream.on('data', parse);

怎么去解析一个 JSON 文件的数据已经解决了,但是如果直接这样使用还是需要再做一些处理工作的。

JSONStream 处理大文件

这里推荐一个 NPM 模块 JSONStream,在它的实现中就是依赖的 jsonparse 这个模块来解析原始的数据,在这基础之上做了一些处理,根据一些匹配模式返回用户想要的数据,简单易用。

下面我们用 JSONStream 解决上面提到的两个问题。

问题一:

假设现在有一个场景,有一个大的 JSON 文件,需要读取每一条数据经过处理之后输出到一个文件或生成报表数据,怎么能够流式的每次读取一条记录?

因为测试,所以我将 highWaterMark 这个值调整了下,现在我们的数据是下面这样的。

[ { "id": 1 }, { "id": 2 }]

重点是 JSONStream 的 parse 方法,我们传入了一个 '.',这个 data 事件也是该模块自己处理过的,每次会为我们返回一个对象:

  • 第一次返回 { id: 1 }
  • 第二次返回 { id: 2 }

const fs = require('fs');const JSONStream = require('JSONStream');(async () => { const readable = fs.createReadStream('./list.json', { encoding: 'utf8', highWaterMark: 10 }) const parser = JSONStream.parse('.'); readable.pipe(parser); parser.on('data', console.log);})()

问题二:

同样一个大的 JSON 文件,我只读取其中的某一块数据,想只取 list 这个数组对象怎么办?

解决第二个问题,现在我们的 JSON 文件是下面这样的。

{ "list": [ { "name": "1" }, { "name": "2" } ], "other": [ { "key": "val" } ]}

与第一个解决方案不同的是改变了 parse('list.*') 方法,现在只会返回 list 数组,other 是不会返回的,其实在 list 读取完成之后这个工作就结束了。

  • 第一次返回 { name: '1' }
  • 第二次返回 { name: '2' }

(async () => { const readable = fs.createReadStream('./list.json', { encoding: 'utf8', highWaterMark: 10 }) const parser = JSONStream.parse('list.*'); readable.pipe(parser); parser.on('data', console.log);})();

总结

当我们遇到类似的大文件需要处理时,尽可能避免将所有的数据存放于内存操作,应用服务的内存都是有限制的,这也不是最好的处理方式。

文中主要介绍如何流式处理类似的大文件,更重要的是掌握编程中的一些思想,例如 SAX 一个核心点就是实现了 “事件驱动” 的设计模式,同时结合 Stream 做到边读取边解析。

处理问题的方式是多样的,还可以在生成 JSON 文件时做拆分,将一个大文件拆分为不同的小文件。

学会寻找答案,NPM 生态发展得还是不错的,基本上你能遇到的问题大多已有一些解决方案了,例如本次问题,不知道如何使用 Stream 来读取一个 JSON 文件时,可以在 NPM 上搜索关键词尝试着找下。

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
现场惨烈,1万乌军遭屠杀殆尽,法国顾问被俘,战场出现重大转变

现场惨烈,1万乌军遭屠杀殆尽,法国顾问被俘,战场出现重大转变

秦蓁
2024-06-08 12:00:07
岛国片中那些无码界顶流女星,作品深入人心,他们不需要马赛克

岛国片中那些无码界顶流女星,作品深入人心,他们不需要马赛克

不二纪录片
2024-06-07 15:04:09
经典国产电影被曝丑化国家,为上戛纳无底线?网友:你看懂了吗?

经典国产电影被曝丑化国家,为上戛纳无底线?网友:你看懂了吗?

毒舌电影
2024-06-07 18:30:07
那英突发意外!

那英突发意外!

鲁中晨报
2024-06-08 20:19:13
附近施工一开始,上海60多岁老房子墙缝变宽,桌上水杯直晃!居民害怕:想要个解释

附近施工一开始,上海60多岁老房子墙缝变宽,桌上水杯直晃!居民害怕:想要个解释

上观新闻
2024-06-08 13:35:41
露馅了!郑伊健在日本被偶遇,本人被曝性格极差,老婆素颜遭吐槽

露馅了!郑伊健在日本被偶遇,本人被曝性格极差,老婆素颜遭吐槽

影孖看世界
2024-06-08 20:10:50
“脱钩”失效!我国降为美国第四大进口国,美却从墨、越大量进口

“脱钩”失效!我国降为美国第四大进口国,美却从墨、越大量进口

财话连篇
2024-06-08 13:26:01
李章洙:以现在韩国队的战略和球员能力,中国队想赢下来肯定很难

李章洙:以现在韩国队的战略和球员能力,中国队想赢下来肯定很难

直播吧
2024-06-08 23:12:08
解放军中将突然去世原因曝光,两女儿均在美国,其弟弟正赶往北京

解放军中将突然去世原因曝光,两女儿均在美国,其弟弟正赶往北京

求实者
2024-06-06 21:45:10
国常会对房地产作出新部署,专家:政策仍有空间,需加大力度

国常会对房地产作出新部署,专家:政策仍有空间,需加大力度

南方都市报
2024-06-08 19:46:08
马斯克星舰奇迹:20亿能做的事只要1000万,中国航天早就破音障了

马斯克星舰奇迹:20亿能做的事只要1000万,中国航天早就破音障了

大风文字
2024-06-08 18:53:59
2013年妻子和堂弟通奸,被丈夫识破,妻子:想阻止我和堂弟,杀掉

2013年妻子和堂弟通奸,被丈夫识破,妻子:想阻止我和堂弟,杀掉

汉史趣闻
2024-06-07 17:36:57
福建舰核心数据遭公布,战力性能超福特级,美专家:落后美70年

福建舰核心数据遭公布,战力性能超福特级,美专家:落后美70年

胖福的小木屋
2024-06-08 01:37:33
星舰第四次试飞取得成功,证明航天业最需要的就是去政治化

星舰第四次试飞取得成功,证明航天业最需要的就是去政治化

回旋镖
2024-06-08 13:57:19
新华社消息|第78届联合国大会协商一致通过中国提出的设立文明对话国际日决议

新华社消息|第78届联合国大会协商一致通过中国提出的设立文明对话国际日决议

新华社
2024-06-08 09:23:50
高考钉子户唐尚珺再次挑战,数学语文成绩不理想准备第17次高考!

高考钉子户唐尚珺再次挑战,数学语文成绩不理想准备第17次高考!

请叫我教育君
2024-06-08 22:35:37
巴西震撼一幕:6万人涌进球场,欢迎39岁传奇回归母队,15年等待

巴西震撼一幕:6万人涌进球场,欢迎39岁传奇回归母队,15年等待

风过乡
2024-06-08 19:38:58
黄一鸣痛斥王思聪拉黑自己不看孩子,王思聪的回应:不要发酒疯

黄一鸣痛斥王思聪拉黑自己不看孩子,王思聪的回应:不要发酒疯

只聊综艺
2024-06-08 23:00:14
99%的人,已经无法再通过高考改变命运了

99%的人,已经无法再通过高考改变命运了

听风听你
2024-06-08 00:03:53
俄代表要求联合国谴责乌克兰侵略行为,俄军事专家称对波兰核打击

俄代表要求联合国谴责乌克兰侵略行为,俄军事专家称对波兰核打击

史政先锋
2024-06-08 13:45:13
2024-06-09 03:28:49
视频剪辑工程师
视频剪辑工程师
将提供最前沿的体育盛事
656文章数 443关注度
往期回顾 全部

科技要闻

今年数学到底有多难?大模型:我也不太会

头条要闻

胖东来董事长于东来回应"患胃癌”:旧视频 现在没问题

头条要闻

胖东来董事长于东来回应"患胃癌”:旧视频 现在没问题

体育要闻

波津:东契奇是最佳球员之一 他G2将会做出更大的回应

娱乐要闻

汤唯抵达巴黎将担任奥运火炬手

财经要闻

重磅详解:为什么美国经济还没有衰退?

汽车要闻

上汽大通大家9售26.99万起 综合续航1300km+

态度原创

时尚
游戏
亲子
房产
本地

北京中轴线上的非遗故事

梦幻西游梧桐空降方总女儿再遭争议,老王携手思聪入驻大唐服?

亲子要闻

求求家长 放过孩子脾胃吧 多吃不一定好

房产要闻

顶流地段+顶级户型!香港半山豪宅,已成为高净值人群的资产压舱石!

本地新闻

我和我的家乡|踏浪营口,心动不止一夏!

无障碍浏览 进入关怀版