专栏首页Web行业观察你所不知道的ndJSON:序列化与管道流

你所不知道的ndJSON:序列化与管道流

一直以为对JSON所有的语法都了如指掌,毕竟json的标准用一只手都数的过来,直到我发现了一个叫ndJSON的标准,简单说,以下2种语法都是合法的:

图一:json格式

图二:ndjson格式

其中图一是常见的json格式,而且整个json对象是一个列表:元素由逗号分隔,再由方括号闭合。图二则是一种称为ndJSON的格式,由换行符(0x0A)分隔每个json对象,最外面也没有闭合字符对。ndjson的mime类型是application/x-ndjson。

需要注意的是,图一和图二并不等同,就是说,图一和图二不仅使用了不同的序列化格式,数据所表达的含义也是不同的。图一只表达了一个对象:一个列表,图二则表达了3个对象:3个“data”字典。这个区别是json和ndjson的本质区别。

NDJSON(ndjson.org)

ndjson(New-line Delimited JSON)是一个比较新的标准,本身超简单,就是一个.ndjson文件中,每行都是一个传统json对象,当然每个json对象中要去掉原本用于格式化的换行符,而json的string中本身就不允许出现换行符(取而代之的是\n),所以ndjson在语法上基本不会出现歧义。但现在问题来了,ndjson有什么用?

JSON流问题(https://en.wikipedia.org/wiki/JSON_streaming)

新的标准总是来自于新的需求。ndjson的出现起源于json流问题。当时,我在设计一个方法用于将mongodb数据库的一张表备份到一个文件中,由于涉及到3个端的数据传输而没有对数据做整体处理的需求,就得使用管道流了。

其实流的概念非常简单,所有的数据传输都是流,都需要把大的数据分割成若干小份然后依次传输,只不过大多情况下传输都是通过底下的api自动完成的,我们感受不到“分割”的过程,也很难感受到“管道传输”的过程。正是这种底层的屏蔽造成了我们的无知,当要我们亲自设计管道的时候就嗝屁了。

在上面这个跨3端管道传输数据流的任务中,需要一边序列化一边走管道,最合适的做法就是将整张表格分割成一个个json对象(无论是sql还是mongo,表中的每一行都可以看成一个json对象),然后通过主机管道流向文件系统。这里出现了一个问题,数据流的最终存在形式是什么?是一个json文件吗?不可能,因为json文件只能表示一个json对象,而数据库表中有若干个对象。那给mysql表中的每一行保存一份json文件?好像也不合适。

HACK JSON

勉强的方法是使用一个json文件存放一份超长的json列表来收纳每一行数据。之所以勉强是因为构造一个json列表需要一些hack技巧:一开始需要写一个‘[’,中间每个json对象之间需要写‘,’,传输完成后又需要一个‘]’,所以我的代码是这样的:

fsWriter.write('[')
mongoReader.on('data', rowObj => {
    fsWriter.write(JSON.stringify(rowObj));
    fsWriter.write(',');
});
mongoReader.on('close', () => {
    // 由于json不允许在最后一个列表元素后面加逗号,hack一个空字典
    fsWriter.write('{}]');
    fsWriter.end();
});

只能说,hack一时爽,一直hack一直爽,天天hack火葬场。通过hack来达到目的是有后遗症的,容易给你带来一堆麻烦事。假如我想在json文件最后插入一条记录或者读取一条记录怎么办?json是作为一个整体来编译处理的,想要读取其中的某一部分也得先编译整个json对象。这是json设计上的一个缺陷,即整体无法直接分割,当然如果你想hack json的话我也不拦你,只是如果想要实现一个通用的方法就得重新设计json流的格式了。带着这个疑问,我想起了程序员3大错觉之一的“我超越了标准库”,于是在维基百科上查了一下原来真的有json流格式。

如图,维基百科介绍了4种不同的json流解决方案,其中第一种就是本文一开始讲到的ndjson,即使用换行符分割的json,由于换行符的特殊性,不会出现歧义:

{"some":"thing\n"}
{"may":{"include":"nested","objects":["and","arrays\n"]}}

ndjson和第二种解决方案比较相似,第二种是通过2个更特殊的控制字符来分割(确切的说是包裹)每一个独立对象,这两个字符是记录分隔符<RS>和行尾反馈符<LF>,这种解决方案利用这2个我前所未闻的控制字符来包裹每个json对象,颇有点超文本标记语言的感觉:

<RS>{"some":"thing"}<LF>
<RS>{
  "may": {
    "include": "nested",
    "objects": [
      "and",
      "arrays"
    ]
  }
}<LF>

图中第三种和第四种方案我就不推荐啦,第三种是不要分隔符,前后2个对象直接相连:

{"some":"thing\n"}{"may":{"include":"nested","objects":["and","arrays"]}}

第四种模仿二进制格式,将对象长度写在前缀里:

18{"some":"thing\n"}55{"may":{"include":"nested","objects":["and","arrays"]}}

这两种方案不仅长相丑陋,而且还容易引起歧义,强烈不推荐使用,而且4种方案中也只有第一种的ndjson实现了标准化,它也是最常用的。当然,这4种都是文本格式的流解决方案,在二进制流领域中问题就简单得多了,比如message pack对象的长度就写在前缀中,对象之后可以直接拼接下一个对象而不会出现任何歧义,就像刚刚的方案三一样。

最后总结一下ndjson对json的性能提升:ndjson使整个文件“流化”,或者说把整个文件分割成许多份,这样避免了整体的束缚,支持局部处理,变得更灵活更快,从而实现了序列化和流传输的同时进行。

参考链接

https://jimmy.blog.csdn.net/article/details/90678160

https://medium.com/@kandros/newline-delimited-json-is-awesome-8f6259ed4b4b

http://ndjson.org/

https://github.com/ndjson/ndjson-spec

本文分享自微信公众号 - WebHub(myWebHub),作者:金恒昱

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-09-16

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • MySQL向MongoDB的妥协之JSON

    MySQL在5.7.8版本中增加了对json数据的支持,而不再是需要使用字符串形式进行存储。下面简单介绍下MySQL对json的操作:

    Jean
  • 从JSON进化到BSON

    自从MEAN引导的JSON数据格式取代传统JAVA推崇的XML以后, json的发展却停滞不前了, 当然这是好事, 因为稳定的结构是不需要向下兼...

    Jean
  • 异步认证与同步认证的分离史

    浏览器cookie是上世纪90年代用于在客户端和服务器间保持短连接的会话机制,但在本世纪的第18年,cookie退出了历史舞台,不信你看现在的http请求方法f...

    Jean
  • Spark SQL操作JSON字段小Tips

    很多时候,比如用structure streaming消费kafka数据,默认可能是得到key,value字段,key是偏移量,value是一个byte数组。很...

    用户2936994
  • python jsonp

    python里面要跨域访问json数据,直接用ajax,后台直接返回json格式的数据是不允许的

    用户2398817
  • PHP 解析JSON 并获取对应的值

    本文由 Alone88 创作,采用 知识共享署名4.0 国际许可协议进行许可 本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名 最后编辑时间为...

    Alone88
  • 用json传递各种东西: img base64/

    本文由腾讯云+社区自动同步,原文地址 https://stackoverflow.club/article/json_transfer_strange_stuf...

    羽翰尘
  • 你所不知道的php json编码解码

    json解析在php,或者说在任何编程语言中都非常常见。但是,你是否知道,json解析在php中是以扩展形式存在。

    写PHP的老王
  • Python 读取 JSON 数据的骚操作

    你想读写 JSON(JavaScript Object Notation) 编码格式的数据。

    数据森麟
  • Python 读取 JSON 数据的骚操作

    你想读写 JSON(JavaScript Object Notation) 编码格式的数据。

    Python知识大全

扫码关注云+社区

领取腾讯云代金券