整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

JSON 的兴起与崛起

自: https://twobithistory.org/2017/09/21/the-rise-and-rise-of-json.html

作者: Two-bit History

译者: runningwater

JSON 已经占领了全世界。当今,任何两个应用程序彼此通过互联网通信时,可以打赌它们在使用 JSON。它已被所有大型企业所采用:十大最受欢迎的 web API 接口列表中(主要由 Google、Facebook 和 Twitter 提供),仅仅只有一个 API 接口是以 XML 的格式开放数据的。 1 这个列表中的 Twitter API 为此做了一个鲜活的注脚:其对 XML 格式的支持到 2013 年结束,其时发布的新版本的 API 取消 XML 格式,转而仅使用 JSON。JSON 也在程序编码级别和文件存储上被广泛采用:在 Stack Overflow(LCTT 译注:一个面向程序员的问答网站)上,现在更多的是关于 JSON 的问题,而不是其他的数据交换格式。 2



XML 仍然在很多地方存在。网络上它被用于 SVG 和 RSS / Atom 信息流。Android 开发者想要获得用户权限许可时,需要在其 APP 的 manifest 文件中声明 —— 此文件是 XML 格式的。XML 的替代品也不仅仅只有 JSON,现在有很多人在使用 YAML 或 Google 的 Protocol Buffers 等技术,但这些技术的受欢迎程度远不如 JSON。目前来看,JSON 是应用程序在网络之间通信的首选协议格式。

考虑到自 2005 年来 Web 编程世界就垂涎于 “异步 JavaScript 和 XML” 而非 “异步 JavaScript 和 JSON” 的技术潜力,你可以发现 JSON 的主导地位是如此的让人惊讶。当然了,这可能与这两种通信格式的受欢迎程度无关,而仅反映出缩写 “AJAX” 似乎比 “AJAJ” 更具吸引力。但是,即使在 2005 年有些人(实际上没有太多人)已经用 JSON 来取代 XML 了,我们不禁要问 XML 的噩运来的如此之快,以至于短短十来年,“异步 JavaScript 和 XML” 这个名称就成为一个很讽刺的误称。那这十来年发生了什么?JSON 怎么会在那么多应用程序中取代了 XML?现在被全世界工程师和系统所使用、依赖的这种数据格式是谁提出的?

JSON 之诞生

2001 年 4 月,首个 JSON 格式的消息被发送出来。此消息是从旧金山湾区某车库的一台计算机发出的,这是计算机历史上重要的的时刻。Douglas Crockford 和 Chip Morningstar 是一家名为 State Software 的技术咨询公司的联合创始人,他们当时聚集在 Morningstar 的车库里测试某个想法,发出了此消息。

在 “AJAX” 这个术语被创造之前,Crockford 和 Morningstar 就已经在尝试构建好用的 AJAX 应用程序了,可是浏览器对其兼容性不好。他们想要在初始页面加载后就将数据传递给应用程序,但其目标要针对所有的浏览器,这就实现不了。

这在今天看来不太可信,但是要记得 2001 年的时候 Internet Explorer(IE)代表了网页浏览器的最前沿技术产品。早在 1999 年的时候,Internet Explorer 5 就支持了原始形式的 XMLHttpRequest,开发者可以使用名为 ActiveX 的框架来访问此对象。Crockford 和 Morningstar 能够使用此技术(在 IE 中)来获取数据,但是在 Netscape 4 中(这是他们想要支持的另一种浏览器)就无法使用这种解决方案。为此 Crockford 和 Morningstar 只得使用一套不同的系统以兼容不同的浏览器。

第一条 JSON 消息如下所示:

<html><head><script>

document.domain = 'fudco';

parent.session.receive(

{ to: "session", do: "test",

text: "Hello world" }

)

</script></head></html>

消息中只有一小部分类似于今天我们所知的 JSON,本身其实是一个包含有一些 JavaScript 的 HTML 文档。类似于 JSON 的部分只是传递给名为 receive() 的函数的 JavaScript 对象字面量。

Crockford 和 Morningstar 决定滥用 HTML 的帧(<frame>)以发送数据。他们可以让一个帧指向一个返回的上述 HTML 文档的 URL。当接收到 HTML 时,JavaScript 代码段就会运行,就可以把数据对象字面量如实地传递回应用程序。只要小心的回避浏览器保护策略(即子窗口不允许访问父窗口),这种技术就可以正常运行无误。可以看到 Crockford 和 Mornginstar 通过明确地设置文档域这种方法来达到其目的。(这种基于帧的技术,有时称为隐藏帧技术,通常在 90 年代后期,即在广泛使用 XMLHttpRequest 技术之前使用。 3

第一个 JSON 消息的惊人之处在于,它显然不是一种新的数据格式的首次使用。它就是 JavaScript!实际上,以此方式使用 JavaScript 的想法如此简单,Crockford 自己也说过他不是第一个这样做的人。他说 Netscape 公司的某人早在 1996 年就使用 JavaScript 数组字面量来交换信息。 4 因为消息就是 JavaScript,其不需要任何特殊解析工作,JavaScript 解释器就可搞定一切。

最初的 JSON 信息实际上与 JavaScript 解释器发生了冲突。JavaScript 保留了大量的关键字(ECMAScript 6 版本就有 64 个保留字),Crockford 和 Morningstar 无意中在其 JSON 中使用了一个保留字。他们使用了 do 作为了键名,但 do 是解释器中的保留字。因为 JavaScript 使用的保留字太多了,Crockford 做了决定:既然不可避免的要使用到这些保留字,那就要求所有的 JSON 键名都加上引号。被引起来的键名会被 JavaScript 解释器识别成字符串,其意味着那些保留字也可以放心安全的使用。这就为什么今天 JSON 键名都要用引号引起来的原因。

Crockford 和 Morningstar 意识到这技术可以应用于各类应用系统。想给其命名为 “JSML”,即JavaScript 标记语言(JavaScript Markup Language)的意思,但发现这个缩写已经被一个名为 Java Speech 标记语言的东西所使用了。因此他们决定采用 “JavaScript Object Notation”,缩写为 JSON。他们开始向客户推销,但很快发现客户不愿意冒险使用缺乏官方规范的未知技术。所以 Crockford 决定写一个规范。

2002 年,Crockford 买下了 JSON.org 域名,放上了 JSON 语法及一个解释器的实例例子。该网站仍然在运行,现在已经包含有 2013 年正式批准的 JSON ECMA 标准的显著链接。在该网站建立后,Crockford 并没有过多的推广,但很快发现很多人都在提交各种不同编程语言的 JSON 解析器实现。JSON 的血统显然与 JavaScript 相关联,但很明显 JSON 非常适合于不同语言之间的数据交换。

AJAX 导致的误会

2005 年,JSON 有了一次大爆发。那一年,一位名叫 Jesse James Garrett 的网页设计师和开发者在博客文章中创造了 “AJAX” 一词。他很谨慎地强调:AJAX 并不是新技术,而是 “好几种蓬勃发展的技术以某种强大的新方式汇集在一起。 5 ” AJAX 是 Garrett 给这种正受到青睐的 Web 应用程序的新开发方法的命名。他的博客文章接着描述了开发人员如何利用 JavaScript 和 XMLHttpRequest 构建新型应用程序,这些应用程序比传统的网页更具响应性和状态性。他还以 Gmail 和 Flickr 网站已经使用 AJAX 技术作为了例子。

当然了,“AJAX” 中的 “X” 代表 XML。但在随后的问答帖子中,Garrett 指出,JSON 可以完全替代 XML。他写道:“虽然 XML 是 AJAX 客户端进行数据输入、输出的最完善的技术,但要实现同样的效果,也可以使用像 JavaScript Object Notation(JSON)或任何类似的结构数据方法等技术。 ” 6

开发者确实发现在构建 AJAX 应用程序时可以很容易的使用 JSON,许多人更喜欢它而不是 XML。具有讽刺意味的是,对 AJAX 的兴趣逐渐的导致了 JSON 的普及。大约在这个时候,JSON 引起了博客圈的注意。

2006 年,Dave Winer,一位高产的博主,他也是许多基于 XML 的技术(如 RSS 和 XML-RPC)背后的开发工程师,他抱怨到 JSON 毫无疑问的正在重新发明 XML。尽管人们认为数据交换格式之间的竞争不会导致某一技术的消亡。其写到:

毫无疑问,我可以编写一个例程来解析 JSON,但来看看他们要重新发明一个东西有多大的意义,出于某种原因 XML 本身对他们来说还不够好(我很想听听原因)。谁想干这荒谬之事?查找一棵树然后把节点串起来。可以立马试试。 7

我很理解 Winer 的挫败感。事实上并没有太多人喜欢 XML。甚至 Winer 也说过他不喜欢 XML。 8 但 XML 已被设计成一个可供任何人使用,并且可以用于几乎能想象到的所有事情。归根到底,XML 实际上是一门元语言,允许你为特定应用程序自定义该领域特定的语言。如 Web 信息流技术 RSS 和 SOAP(简单对象访问协议)就是例子。Winer 认为由于通用交换格式所带来的好处,努力达成共识非常重要。XML 的灵活性应该能满足任何人的需求,然而 JSON 格式呢,其并没有比 XML 提供更多东西,除了它抛弃了使 XML 更灵活的那些繁琐的东西。

Crockford 阅读了 Winer 的这篇文章并留下了评论。为了回应 JSON 重新发明 XML 的指责,Crockford 写到:“重造轮子的好处是可以得到一个更好的轮子。” 9

JSON 与 XML 对比

到 2014 年,JSON 已经由 ECMA 标准和 RFC 官方正式认可。它有了自己的 MIME 类型。JSON 已经进入了大联盟时代。

为什么 JSON 比 XML 更受欢迎?

在 JSON.org 网站上,Crockford 总结了一些 JSON 的优势。他写到,JSON 的语法极少,其结构可预测,因此 JSON 更容易被人类和机器理解。 10 其他博主一直关注 XML 的冗长啰嗦及“尖括号负担”。 11XML 中每个开始标记都必须与结束标记匹配,这意味着 XML 文档包含大量的冗余信息。在未压缩时,XML 文档的体积比同等信息量 JSON 文档的体积大很多,但是,更重要的,这也使 XML 文档更难以阅读。

Crockford 还声称 JSON 的另一个巨大优势是其被设计为数据交换格式。 12 从一开始,它的目的就是在应用程序间传递结构化信息的。而 XML 呢,虽然也可以使用来传递数据,但其最初被设计为文档标记语言。它演变自 SGML(通用标准标记语言),而它又是从被称为 Scribe 的标记语言演变而来,其旨在发展成类似于 LaTeX 一样的文字处理系统。XML 中,一个标签可以包含有所谓的“混合内容”,即带有围绕单词、短语的内嵌标签的文本。这会让人浮现出一副用红蓝笔记录的手稿画面,这是标记语言核心思想的形象比喻。另一方面,JSON 不支持对混合内容模型的清晰构建,但这也意味着它的结构足够简单。一份文档最好的建模就是一棵树,但 JSON 抛弃了这种文档的思想,Crockford 将 JSON 抽象限制为字典和数组,这是所有程序员构建程序时都会使用的最基本的、也最熟悉的元素。

最后,我认为人们不喜欢 XML 是因为它让人困惑。它让人迷惑的地方就是有很多不同的风格。乍一看,XML 本身及其子语言(如 RSS、ATOM、SOAP 或 SVG)之间的界限并不明显。典型的 XML 文档的第一行标识了该 XML 的版本,然后该 XML 文档应该符合特定的子语言。这就有变化需要考虑了,特别是跟 JSON 做比较,JSON 是如此简单,以至于永远不需要编写新版本的 JSON 规范。XML 的设计者试图将 XML 做为唯一的数据交换格式以支配所有格式,就会掉入那个经典的程序员陷阱:过度工程化。XML 非常笼统及概念化,所以很难于简单的使用。

在 2000 年的时候,发起了一场使 HTML 符合 XML 标准的活动,发布了一份符合 XML 标准的 HTML 开发规范,这就此后很出名的 XHTML。虽然一些浏览器厂商立即开始支持这个新标准,但也很明显,大部分基于 HTML 技术的开发者不愿意改变他们的习惯。新标准要求对 XHTML 文档进行严格的验证,而不是基于 HTML 的基准。但大多的网站都是依赖于 HTML 的宽容规则的。到 2009 年的时候,试图编写第二版本的 XHTML 标准的努力已经流产,因为未来已清晰可见,HTML 将会发展为 HTML5(一种不强制要求接受 XML 规则的标准)。

如果 XHTML 的努力取得了成功,那么 XML 可能会成为其设计者所希望的通用数据格式。想象一下,一个 HTML 文档和 API 响应具有完全相同结构的世界。在这样的世界中,JSON 可能不会像现在一样普遍存在。但我把 HTML 的失败看做是 XML 阵营的一种道义上的失败。如果 XML 不是 HTML 的最佳工具,那么对于其他应用程序,也许会有更好的工具出现。在这个世界,我们的世界,很容易看到像 JSON 格式这样的足够简单、量体裁衣的才能获得更大的成功。

如果你喜欢这博文,每两周会更新一次! 请在 Twitter 上关注 @TwoBitHistory 或订阅 RSS feed ,以确保得到更新的通知。


  1. http://www.cs.tufts.edu/comp/150IDS/final_papers/tstras01.1/FinalReport/FinalReport.html#software ↩
  2. https://insights.stackoverflow.com/trends?tags=json%2Cxml%2Cprotocol-buffers%2Cyaml%2Ccsv ↩
  3. Zakas, Nicholas C., et al. “What Is Ajax?” Professional Ajax, 2nd ed., Wiley, 2007. ↩
  4. https://youtu.be/-C-JoyNuQJs?t=32s ↩
  5. http://adaptivepath.org/ideas/ajax-new-approach-web-applications/ ↩
  6. 同上 ↩
  7. http://scripting.com/2006/12/20.html ↩
  8. http://blogoscoped.com/archive/2009-03-05-n15.html ↩
  9. https://scripting.wordpress.com/2006/12/20/scripting-news-for-12202006/#comment-26383 ↩
  10. http://www.json.org/xml.html ↩
  11. https://blog.codinghorror.com/xml-the-angle-bracket-tax ↩
  12. https://youtu.be/-C-JoyNuQJs?t=33m50sgg ↩

via: https://twobithistory.org/2017/09/21/the-rise-and-rise-of-json.html

作者: Two-Bit History 选题: lujun9972 译者: runningwater 校对: wxy

本文由 LCTT 原创编译, Linux中国 荣誉推出

点击“了解更多”可访问文内链接

者:andruzhang,腾讯IEG后台开发工程师

| 导语 JSON,一个伟大的协议,前端工程师的卓越发明!相信 99% 的程序员都认识 JSON。但这么简单的 JSON,也有坑!相信我,看完这篇文章,你就可以吃透这个可爱的 JSON 了。

相信 99% 的程序员都认识 JSON,它作为前后端交互的热门协议,因其易理解、简单、灵活和超强的可读性,得到了互联网的广泛欢迎,甚至很多微服务之间的传输协议中也得到应用。

但是笔者在开发一个 Go 的 JSON 编解码库的过程中,除了自己趟过各种奇奇怪怪的问题之外,也认识到广大程序员们对 JSON 各种奇奇怪怪的用法和使用姿势。在处理解决这些问题之后,笔者萌生了对 JSON 进行进一步科普和介绍的想法。

相信我,看完这篇文章,你就可以吃透这个可爱的 JSON 了。

这不是万字长文,所以答应我,不要 TLDR(too long, don't read) 好吗?


JSON 是什么

这个问题似乎很容易回答:JavaScript Object Notation,直译就是 JavaScript 对象表示。

然而,这个命名中的 “JavaScript” 是个很大的误导,让人以为 JSON 是附属于 JavaScript 的。其实不然,JSON 是完全独立于任何语言之上的一个对象表示协议,甚至从我个人的角度来说,它非常的不 “JS”。

关于 JSON 的 “常识”

从大家的认知中,相信以下的几点是常识:

  • JSON 可以是对象(object),使用 {...} 格式包起来
  • JSON 可以是是数组(array),使用 [...] 格式包起来
  • JSON 内的值可以是 string, boolean, number,也可以进一步嵌套 object 和 array
  • JSON 也有特殊字符需要转义,最显而易见的就是双引号 "、反斜杠 \、换行符 \n\r
  • JSON object 的键(key)必须是 string 格式
  • JSON 可以通过 object 和 array 类型实现无限层级的嵌套

好了,懂了上面几点,其实也就弄懂了 JSON 90% 甚至是 99% 的应用场景了。程序员们也足以可以实现简单的 JSON 编码逻辑。

如果你想知道剩下那些让人掉大牙的 1%,欢迎你往下看;如果你想要自己开发一个 JSON 编解码库,以下内容也能够让你少走很多弯路:


JSON 标准规定了什么

在了解各种 JSON 的坑之前,我们先来了解一下 JSON 标准本身。

现行通行的 JSON 标准是 ECMA-404(也可参见附件),这篇协议总共有14页,但除去封面、封底、目录、简介、版权声明,正文只有5页,并且其中3页大部分是图片。所以笔者推荐所有的程序员都把这篇文档通读一遍,恐怕这是大部分人唯一能完整读完的主流协议了(狗头)。

所以啊,“可爱” 的 JSON 可真不是标题党——试想这么短小的协议,怎不可谓可爱呢!

通读了文档之后我们可以发现,除了前文提及的几个常识之外,下面有几个知识点估计大家很少留意:

  • JSON 是用来承载 unicode 字符的,这一点在标准中明确提及
  • JSON 标准中其实并没有 boolean 这个类型,但是 truefalse 被并列为单独的两个类型
  • 作为最外层的 JSON 类型,并不限定为 object 或 array,实际上 string, boolean, number, 甚至 null 也是可以的
  • JSON 数字表示可以使用科学计数法,可能许多人在实际应用中没留意过
  • JSON 明确说明不支持 +/-Inf 和 NaN 这两组在 IEEE 754 中规定的特殊数值

我们解读一下上面这些知识点带来的影响:

  • unicode 字符:

JSON 传输的应当是可视化字符,而不应该也无法承载不可读的二进制数据

换句话说,请尽量不要用 JSON 来传输二进制数据

  • 没有 boolean 类型

这个问题不大,主要是对各种库的使用上——有些库提供了 boolean 类型分类,而有些库则按照标准协议分为 true 和 false 两种类型,请注意区分

  • 外层类型不限定

其实这影响不大,但是这使得 JSON 多了一个额外功能: 当我们要把包含换行符的文本压在一行内,但又要保持高可读性的时候,我们可以将文本序列化为 JSON

这个特性在打日志的时候特别有用

  • 科学计数法:

这主要是在解析 JSON 数据时,需要注意兼容

  • 特殊浮点值:

这个问题可大可小,大部分情况下不会遇到,但是一旦出现了,会导致整个序列化过程失败。开发者们需要谨慎处理浮点数,下文会进一步提及


JSON 没有规定什么?

  • JSON 并没有严格限定文本的编码格式
  • JSON 数字是十进制的,没有限制绝对值大小,也没有限制小数点后的位数
  • JSON 没有明确规定 ASCII 的控制字符和不可见字符的传输格式
  • JSON 没有限制 object 的 key 所使用的字符
  • JSON 没有明确说明 object 的 key 之间是有序的还是无序的

为什么列出这几点?让我们继续往下看:


JSON 的常见 “坑位”

JSON 如此简单,但也正因为它的特性,我们会不知不觉地落入一些圈套中。下面我列几个常见的坑,读者看看能不能对号入座:

没搞明白编码格式导致解码出错

前面说到,JSON 明确声明自己是用于承载 unicode 的。但是,unicode 除了规定每个字符码的含义(码点)之外,还包含另外一个重要规范,那就是如何将这些字符串成字符流,这就是我们常说的 UTF-8、UTF-16BE、 UTF-16LE 等等概念。而 JSON 并没有对此作明确限定。这就导致了在 JSON 的编码与解码端,如果没有约定好,那么就会出现乱码。

笔者曾经与一个合作伙伴的开发工程师对接过 JSON,对方使用 Java 解码我发出的原始数据时出现乱码。我告诉对方,应该用 UTF-8 格式解码,但是对方不明白 UTF-8 是什么,只是不停的告诉我他使用的是哪一个 Java 函数。

我的解决方案不敢说万能,但应该即便是上古的解码器都能处理——这个方案就是指定各编码器在编码时,对大于 ASCII 范围的字符均作转义处理为 \uXXXX 格式。这么一通操作后,我的合作伙伴表示:程序通了。

其实在 JSON 规范中,列举了不少篇幅说明大于 U+00FF 的码点应该如何转义,包括大于 U+FFFF 的。所以从笔者的个人观点看来,如果我们严格按照 JSON 规范的话,什么 UTF-8,GB18030 等编码格式都是未被允许的,唯一严格允许的就是 \uXXXX 转义。但是在实际操作中,这种转义太浪费字节序列了,各种语言对 string 类型进行操作时,习惯性地按照本身的字符串在内存中的默认编码格式照搬到 JSON 序列化上了。

如果 JSON 的编码端无法确保或协调对端解码器的编码格式,那么请统一使用 \uXXXX 转义。

JSON 中的 UTF-16

如果读者不需要自行编码或解析 JSON 数据的话,可以跳过这一小节;否则,这一段是必修课。

对于编码值大于 127 的字符来说,如上文所示,我们可以转义为 \uXXXX 格式。那么是不是直接写 sprintf("\u04X", aByte) 就可以呢?

如果你这么做,那么作为一个通用库来说……

严格来说,\uXXXX 其实是对 UTF-16 编码的转写。这是一个比较少用的编码格式。我们都知道,UTF-8 是一个变长的编码格式,它编码的基本单位是 1 个字节。受早期 Windows 16位 wchar 的影响,有些人可能会误以为UTF-16 是定长的 2 个字节。其实并不然,对于大于 65535 的 unicode 码点,UTF-16 使用 4 个字节编码,而 JSON 只需要将编码后的两个半字(half world)按顺序使用 \uXXXX 转写出来就可以了。

对 JSON 具体需要转义的字符,以及 UTF-16 的相关内容,笔者之前也写过一篇文章专门说明,欢迎移步。

ASCII 控制字符

按理说,JSON 只应该承载可见字符。但是按照 JSON 的规范,JSON 承载的是 unicode,而 ASCII 控制字符也是 unicode 的一部分,所以 JSON 也是可以承载 ASCII 控制字符的。

其实这个问题并不大,即便把这些控制字符原封不动地包装在 JSON 序列化之后的数据流中,对端也是可以正确解析出来的。大家要注意的是,如果带控制字符的话,数据渲染到终端时,某些控制字符可能不会被渲染出来。如果此时你从终端复制一段数据,在粘贴到别处,这些字符可能就都丢失了。


老生常谈的浮点数

精度问题

众所周知,在许多语言的内部处理逻辑中,带小数部分的数字是使用浮点数来处理的。对于小数部分无法被 2 除尽的十进制数,系统(为了照顾 “你们人类”)而使用二进制浮点数的近似值来表示。

具体到 JSON 中,坑在哪里?其实吧这里不算是 JSON 的坑,而是一个通用的问题。我简单提一下吧:

首先我们知道,对很多强类型语言来说,浮点数往往可以细分为单精度和双精度两种,前者使用 4 个字节,后者使用 8 个字节。单精度在有效位数方面比双精度数小一大截,但是在具体实践中,考虑到数据传输、计算效率、数值范围,往往单精度就足矣。

这个时候,如果一个浮点数在系统内部经过各种不同精度的转换之后,在转换成 JSON 时会有什么问题呢?我们来考虑一下的过程:

  • 一个十进制精确定点数值 2.1
  • 使用单精度浮点数表示,f = float32(2.1)
  • 调用某些接口,可能接口本身是不支持单精度数,因此转成了双精度处理 d = float64(f)
  • 将这个双精度数填入一个结构体并且格式化为 JSON 小数输出

此时,我们会得到什么数字呢?根据不同的语言,输出可能会不同。如果不指定精度的话,很多 JSON 编码库是支持根据浮点数的具体数值,猜测并且格式化为一个最接近的十进制小数。以 Go 为例子,我们会发现通过 JSON 输出的时候,这个 2.1 变成了 2.0999999046325684

这在本质上,是因为单精度数经过一次类型转换为双精度后,其二进制有效位数以零填充,转为十进制时,对于双精度浮点数,这就不再是双精度有效数字下的 2.1 了。

换句话说,开发者们在处理浮点数时,需要考虑不同精度浮点数的精度处理差异,特别是金融相关的数据计算和传输,一不小心就会造成大量的对账错误。

特殊浮点数

前文提及,JSON 明确说明不支持 +/-Inf 和 NaN 这两组在 IEEE 754 中规定的特殊数值。但有一些数学运算库,在计算之后会将奇点输出为 +/-Inf 或 NaN,对于很多 JSON 编码库来说,遇到这种数值会导致整个数据编码失败。因此开发者需要针对这种情况特殊处理。我开发的 jsonvalue 中就有这样的一个专题。毕竟是笔者在实际操作中趟过的坑……


有顺序的 K-V

在 JSON 规范中,明确强调 array 类型的子值顺序的重要性(这很好理解)。

但是针对 object 类型,key 的顺序则未提及。在实际操作中我发现不少应用场景中把 object 的 K-V 也当作有序数据来操作了——这在很多自己使用代码简单拼接 JSON 串的场景中,出乎意料地很常见。

还请各位明确注意:JSON 的 object,我们应当默认它是无序的。如果需要传递一系列有序的 KV 对,那么请务必使用 array 类型,不要再用 object 了,这绝对不是一个通用的做法。

在这一点上,我自己也犯过一个很低级的错误:

JSON 数据的幂等检查和数据校验

年少无知的我有一次设计过一个模块,接收上游发来的各种事件信息。为了确保事件都被处理,因此当下游响应不及时时,上游可能会将同一事件重复发出。此时我需要对事件进行幂等计算,确保同一事件不会被重复处理。

一开始我这是简单对上游数据进行 hash 计算。但是在实际运作了一段时间,出了 bug,而原因也很简单,我们看看下面两段数据:

  • {"time":1601539200,"event_id":10,"openid":"abcdefg"}
  • {"event_id":10,"time":1601539200,"openid":"abcdefg"}

这两段数据仅仅是 key 的顺序不同而已,但如果使用上面的逻辑,这数据根本就是一模一样的!我们永远要注意:如果我们没有明确地与上游约定好的话,那么请永远不要对上游做任何假设;即便使用文档约束,也依然要多多检查各种例外情况。

结果怎么解决?约束上游?这岂不是显得我能力不行嘛(狗头,主要是不想让上游知道我的 bug 这么 low),所以我在我自己这边简单对解析出来的 key 排序之后(反正 key 不多且无嵌套),再重新计算 hash 来解决。


结语

本文从 JSON 标准出发,结合自己的一些工作经验,整理了 JSON 编解码过程中的一些坑和注意点。如果本文有谬误,还请不吝指正;如果读者还遇到了其他的坑,也欢迎补充。

此外,如果读者中有 Go 开发者的话,也欢迎了解一下我的 jsonvalue 库,点个 star 或者给我提 issue 都非常欢迎~~

参考资料

JSON 相关资料:

  • Python JSON模块解码中文的BUG
  • 既然 GB18030 可以是 Unicode 的 UTF 转写,为什么中国境内不强制使用该字符集?
  • Go json 踩坑记录
  • axgle/mahonia
  • golang:gbk/gb18030编码字符串与utf8字符串互转
  • GB 18030

在线实例

通过我们的编辑器,您可以在线编辑 JavaScript 代码,然后通过点击一个按钮来查看结果:

JSON 实例

<!DOCTYPEhtml><html><head><metacharset="utf-8"><title>菜鸟教程(runoob.com)</title></head><body><h2>JavaScript 创建 JSON 对象</h2><p>网站名称: <spanid="jname"></span><br/> 网站地址: <spanid="jurl"></span><br/> 网站 slogan: <spanid="jslogan"></span><br/></p><script>

varJSONObject= {"name":"菜鸟教程", "url":"www.runoob.com", "slogan":"学的不仅是技术,更是梦想!"};document.getElementById("jname").innerHTML=JSONObject.namedocument.getElementById("jurl").innerHTML=JSONObject.urldocument.getElementById("jslogan").innerHTML=JSONObject.slogan

</script></body></html>

与 XML 相同之处

  • JSON 是纯文本

  • JSON 具有"自我描述性"(人类可读)

  • JSON 具有层级结构(值中存在值)

  • JSON 可通过 JavaScript 进行解析

  • JSON 数据可使用 AJAX 进行传输

与 XML 不同之处

  • 没有结束标签

  • 更短

  • 读写的速度更快

  • 能够使用内建的 JavaScript eval() 方法进行解析

  • 使用数组

  • 不使用保留字

为什么使用 JSON?

对于 AJAX 应用程序来说,JSON 比 XML 更快更易使用:

使用 XML

  • 读取 XML 文档

  • 使用 XML DOM 来循环遍历文档

  • 读取值并存储在变量中

使用 JSON

  • 读取 JSON 字符串

  • 用 eval() 处理 JSON 字符串

如您还有不明白的可以在下面与我留言或是与我探讨QQ群308855039,我们一起飞!