1 OpenAI 探索用较低级的 AI 来监管更高级的 AIOpenAI 的团队介绍了一种让功能较弱的大型语言模型（LLM）监督功能较强的 LLM 的技术，并表示，这可能是朝着弄清人类如何监督超人机器迈出的一小步。OpenAI 团队认为，机器的最终优势是必然的，“我们一直在碾压所有的基准，而且这种进步有增无减”。他们没有研究人类如何监督超人机器，而是研究了用其五年前发布的模型 GPT-2 如何监督最新、最强大的模型 GPT-4。“如果你能做到这一点，那就证明你可以使用类似的技术让人类监督超过人类的模型……结果喜忧参半”。他们的结论是，这种方法很有前途，但还需要更多的努力。（插图：DA/fc35d211-8080-400d-b6f9-0e6a5bd31cf3）消息来源：MIT 科技评论老王点评：这就是用一个比较“愚忠”的 AI 来监管那些“桀骜不驯”的 AI 吗？2 微软发布小型 LLM Phi-2，性能超越更大的 Llama 2Phi-2 拥有 27 亿个参数，其性能可与其他更大的模型相媲美，包括拥有 70 亿个参数的 Meta 的 Llama 2-7B，以及另一个也拥有 70 亿个参数的模型 Mistral-7B。微软称，尽管 Phi-2 比谷歌全新的 Gemini Nano 2 模型多了 5 亿个参数，但其性能却优于后者，而且其响应的 “毒性” 和偏差也小于 Llama 2。不过目前，微软限制 Phi-2 只能用于 “研究目的”，而不能用于商业用途。（插图：DA/71166394-aa5d-4ed1-b10f-aeff651173cf）消息来源：Venture Beat老王点评：相信随着研究的进展，以后运行在手机上或边缘设备上的超级 AI 将不再遥远。3 JSON 的创造者发布新编程语言 Misty创造了 JSON 符号的 道格·克罗克福德 Doug Crockford 又创造了一种“既适合学生使用，也适合专业程序员使用”的新的编程语言。该语言称其为 “一种动态的、通用的、过渡性的演员语言。它的语法温和，旨在为学生提供帮助，同时还具有一些高级特性，如能力安全和具有词法范围的 Lambda。”有趣的是，为了避免因缩进不同而导致的争论，Misty 只允许一种约定：缩进是 4 个空格。（插图：DA/9bb516c9-62a0-40a8-826c-5407a4a5ea82）消息来源：Doug Crockford老王点评：一种新的 BASIC 语言吗？
抛开关于是否使用 JSON 作为配置格式的争论，只需学习如何用 Groovy 来解析它。应用程序通常包括某种类型的默认或“开箱即用”的状态或配置，以及某种让用户根据自己的需要定制配置的方式。例如，LibreOffice Writer 通过其菜单栏上的工具 > 选项，可以访问诸如用户数据、字体、语言设置等（以及更多的）设置。一些应用程序（如 LibreOffice）提供了一个点选式的用户界面来管理这些设置。有些，像 Tracker（GNOME 的“任务”，用于索引文件）使用 XML 文件。还有一些，特别是基于 JavaScript 的应用，使用 JSON，尽管它有许多人抗议（例如，这位作者 和 这位其他作者）。在这篇文章中，我将回避关于是否使用 JSON 作为配置文件格式的争论，并解释如何使用 Groovy 编程语言 来解析这类信息。Groovy 以 Java 为基础，但有一套不同的设计重点，使 Groovy 感觉更像 Python。安装 Groovy由于 Groovy 是基于 Java 的，它也需要安装 Java。你可能会在你的 Linux 发行版的软件库中找到最近的、合适的 Java 和 Groovy 版本。或者，你可以按照其网站上的 说明 安装 Groovy。 Linux 用户的一个不错的选择是 SDKMan，你可以使用它来获取 Java、Groovy 和许多其他相关工具的多个版本。 对于本文，我将使用我的发行版的 OpenJDK11 和 SDKMan 的 Groovy 3.0.7。演示的 JSON 配置文件在这个演示中，我从 Drupal 中截取了这个 JSON 文件，它是 Drupal CMS 使用的主要配置文件，并将其保存在文件 config.json 中：{ "vm": { "ip": "192.168.44.44", "memory": "1024", "synced_folders": [ { "host_path": "data/", "guest_path": "/var/www", "type": "default" } ], "forwarded_ports": [] }, "vdd": { "sites": { "drupal8": { "account_name": "root", "account_pass": "root", "account_mail": "box@example.com", "site_name": "Drupal 8", "site_mail": "box@example.com", "vhost": { "document_root": "drupal8", "url": "drupal8.dev", "alias": ["www.drupal8.dev"] } }, "drupal7": { "account_name": "root", "account_pass": "root", "account_mail": "box@example.com", "site_name": "Drupal 7", "site_mail": "box@example.com", "vhost": { "document_root": "drupal7", "url": "drupal7.dev", "alias": ["www.drupal7.dev"] } } } } } 这是一个漂亮的、复杂的 JSON 文件，有几层结构，如：<>.vdd.sites.drupal8.account_name 和一些列表，如：<>.vm.synced_folders 这里，<> 代表未命名的顶层。让我们看看 Groovy 是如何处理的。用 Groovy 解析 JSONGroovy 自带的 groovy.json 包，里面有各种很酷的东西。其中最好的部分是 JsonSlurper 类，它包括几个 parse() 方法，可以将 JSON 转换为 Groovy 的 Map，一种根据键值存储的数据结构。下面是一个简短的 Groovy 程序，名为 config1.groovy，它创建了一个 JsonSlurper 实例，然后调用其中的 parse() 方法来解析文件中的 JSON，并将其转换名为 config 的 Map 实例，最后将该 map 输出：import groovy.json.JsonSlurper def jsonSlurper = new JsonSlurper() def config = jsonSlurper.parse(new File('config.json')) println "config = $config" 在终端的命令行上运行这个程序：$ groovy config1.groovy config = [vm:[ip:192.168.44.44, memory:1024, synced_folders:host_path:data/, guest_path:/var/www, type:default, forwarded_ports:[]], vdd:[sites:[drupal8:[account_name:root, account_pass:root, account_mail:box@example.com, site_name:Drupal 8, site_mail:box@example.com, vhost:[document_root:drupal8, url:drupal8.dev, alias:[www.drupal8.dev]]], drupal7:[account_name:root, account_pass:root, account_mail:box@example.com, site_name:Drupal 7, site_mail:box@example.com, vhost:[document_root:drupal7, url:drupal7.dev, alias:[www.drupal7.dev]]]]]] $ 输出显示了一个有两个键的顶层映射：vm 和 vdd。每个键都引用了它自己的值的映射。注意 forwarded_ports 键所引用的空列表。这很容易，但它所做的只是把东西打印出来。你是如何获得各种组件的呢？下面是另一个程序，显示如何访问存储在 config.vm.ip 的值：import groovy.json.JsonSlurper def jsonSlurper = new JsonSlurper() def config = jsonSlurper.parse(new File('config.json')) println "config.vm.ip = ${config.vm.ip}" 运行它：$ groovy config2.groovy config.vm.ip = 192.168.44.44 $ 是的，这也很容易。 这利用了 Groovy 速记，这意味着：config.vm.ip 在 Groovy 中等同于：config['vm']['ip'] 当 config 和 config.vm 都是 Map 的实例，并且都等同于在 Java 中的:config.get("vm").get("ip") 仅仅是处理 JSON 就这么多了。如果你想有一个标准的配置并让用户覆盖它呢？在这种情况下，你可能想在程序中硬编码一个 JSON 配置，然后读取用户配置并覆盖任何标准配置的设置。假设上面的配置是标准的，而用户只想覆盖其中的一点，只想覆盖 vm 结构中的 ip 和 memory 值，并把它放在 userConfig.json 文件中:{ "vm": { "ip": "201.201.201.201", "memory": "4096", } } 你可以用这个程序来做：import groovy.json.JsonSlurper def jsonSlurper = new JsonSlurper() // 使用 parseText() 来解析一个字符串，而不是从文件中读取。 // 这给了我们一个“标准配置” def standardConfig = jsonSlurper.parseText(""" { "vm": { "ip": "192.168.44.44", "memory": "1024", "synced_folders": [ { "host_path": "data/", "guest_path": "/var/www", "type": "default" } ], "forwarded_ports": [] }, "vdd": { "sites": { "drupal8": { "account_name": "root", "account_pass": "root", "account_mail": "box@example.com", "site_name": "Drupal 8", "site_mail": "box@example.com", "vhost": { "document_root": "drupal8", "url": "drupal8.dev", "alias": ["www.drupal8.dev"] } }, "drupal7": { "account_name": "root", "account_pass": "root", "account_mail": "box@example.com", "site_name": "Drupal 7", "site_mail": "box@example.com", "vhost": { "document_root": "drupal7", "url": "drupal7.dev", "alias": ["www.drupal7.dev"] } } } } } """) // 打印标准配置 println "standardConfig = $standardConfig" //读入并解析用户配置信息 def userConfig = jsonSlurper.parse(new File('userConfig.json')) // 打印出用户配置信息 println "userConfig = $userConfig" // 一个将用户配置与标准配置合并的函数 def mergeMaps(Map input, Map merge) { merge.each { k, v -> if (v instanceof Map) mergeMaps(input[k], v) else input[k] = v } } // 合并配置并打印出修改后的标准配置 mergeMaps(standardConfig, userConfig) println "modified standardConfig $standardConfig" 以下列方式运行：$ groovy config3.groovy standardConfig = [vm:[ip:192.168.44.44, memory:1024, synced_folders:host_path:data/, guest_path:/var/www, type:default, forwarded_ports:[]], vdd:[sites:[drupal8:[account_name:root, account_pass:root, account_mail:box@example.com, site_name:Drupal 8, site_mail:box@example.com, vhost:[document_root:drupal8, url:drupal8.dev, alias:[www.drupal8.dev]]], drupal7:[account_name:root, account_pass:root, account_mail:box@example.com, site_name:Drupal 7, site_mail:box@example.com, vhost:[document_root:drupal7, url:drupal7.dev, alias:[www.drupal7.dev]]]]]] userConfig = [vm:[ip:201.201.201.201, memory:4096]] modified standardConfig [vm:[ip:201.201.201.201, memory:4096, synced_folders:host_path:data/, guest_path:/var/www, type:default, forwarded_ports:[]], vdd:[sites:[drupal8:[account_name:root, account_pass:root, account_mail:box@example.com, site_name:Drupal 8, site_mail:box@example.com, vhost:[document_root:drupal8, url:drupal8.dev, alias:[www.drupal8.dev]]], drupal7:[account_name:root, account_pass:root, account_mail:box@example.com, site_name:Drupal 7, site_mail:box@example.com, vhost:[document_root:drupal7, url:drupal7.dev, alias:[www.drupal7.dev]]]]]] $ 以 modified standardConfig 开头的一行显示，vm.ip and vm.memory 的值被覆盖了。眼尖的读者会注意到，我没有检查畸形的 JSON，也没有仔细确保用户的配置是有意义的（不创建新字段，提供合理的值，等等）。所以用这个递归方法来合并两个映射在现实中可能并不那么实用。好吧，我必须为家庭作业留下 一些 东西，不是吗？Groovy 资源Apache Groovy 网站有很多很棒的 文档。另一个很棒的 Groovy 资源是 Mr. Haki。学习 Groovy 的一个非常好的理由是继续学习 Grails，它是一个非常高效的全栈 Web 框架，建立在 Hibernate、Spring Boot 和 Micronaut 等优秀组件之上。via: https://opensource.com/article/21/6/groovy-parse-json作者：Chris Hermansen 选题：lujun9972 译者：geekpi 校对：wxy本文由 LCTT 原创编译，Linux中国 荣誉推出
JSON 文件非常棒，因为它们以人类可读的格式存储数据集合。然而，如果 JSON 文件被最小化过，那么阅读 JSON 文件可能会很痛苦。以这个为例：计算机可以很容易地读取它。即使是人也能读，但如果 JSON 文件以合适的格式显示，那么阅读就会简单很多。我的意思是 JSON 文件应该是这样读的：你可以使用大多数的文本编辑器和一些插件以合适的格式显示它。然而，如果你在终端中，或者你想在你的 shell 脚本中这么做，事情会有所不同。如果你有一个已最小化过的 JSON 文件，让我来告诉你如何在 Linux 终端中漂亮地输出它。在 Linux 中用 jq 命令漂亮地打印 JSON 文件jq 是一个命令行 JSON 处理器。你可以用它来切分、过滤、映射和转换结构化数据。我在这里不打算详细介绍 jq 命令行工具的使用。要使用 jq，你需要先安装它。你可以使用你的发行版的包管理器来安装它。如果启用了 universe 仓库，你可以使用 apt 命令在 Ubuntu 上安装它：sudo apt install jq 安装好后，用下面的方法在显示屏上漂亮地打印 JSON 文件：jq . sample.json 你可能也想用 cat，但我认为 cat 在这里没用。cat sample.json | jq 请记住，上述命令不会影响原始 JSON 文件。不会向它写入任何更改。你可能已经知道如何在 Linux 中把命令输出重定向到一个文件。你可能也知道不能重定向到同一个文件，而且 tee 命令也不能保证一直有效。如果你想用漂亮的格式修改原来的 JSON 文件，可以把解析后的输出结果用管道传送到一个新的文件中，然后覆盖原来的 JSON 文件。jq . sample.json > pretty.json 额外技巧：用 jq 命令对 JSON 文件最小化。让我们反过来，对一个格式良好的 JSON 文件进行最小化。要最小化 JSON 文件，你可以使用选项 -c。jq -c < pretty.json 如果你愿意，你也可以使用 cat 和重定向：cat pretty.json | jq -c 在 Linux 中使用 Python 来漂亮地打印 JSON 文件你更有可能是在系统中安装了 Python。如果是这样的话，你可以用它在终端漂亮地打印 JSON 文件：python3 -m json.tool sample.json 我知道还有其他方法可以解析 JSON 文件并以适当的格式打印出来。你可以自己去探索，但这两种方法足以完成漂亮地打印 JSON 文件的工作。via: https://itsfoss.com/pretty-print-json-linux/作者：Abhishek Prakash 选题：lujun9972 译者：geekpi 校对：wxy本文由 LCTT 原创编译，Linux中国 荣誉推出
在以不同语言编写并在不同平台上运行的应用程序之间交换数据时，Protobuf 编码可提高效率。 协议缓冲区 Protocol Buffers （Protobufs）像 XML 和 JSON 一样，可以让用不同语言编写并在不同平台上运行的应用程序交换数据。例如，用 Go 编写的发送程序可以在 Protobuf 中对以 Go 表示的销售订单数据进行编码，然后用 Java 编写的接收方可以对它进行解码，以获取所接收订单数据的 Java 表示方式。这是在网络连接上的结构示意图：Go 销售订单 —> Pbuf 编码 —> 网络 —> Pbuf 界面 —> Java 销售订单与 XML 和 JSON 相比，Protobuf 编码是二进制而不是文本，这会使调试复杂化。但是，正如本文中的代码示例所确认的那样，Protobuf 编码在大小上比 XML 或 JSON 编码要有效得多。Protobuf 以另一种方式提供了这种有效性。在实现级别，Protobuf 和其他编码系统对结构化数据进行 序列化 serialize 和 反序列化 deserialize 。序列化将特定语言的数据结构转换为字节流，反序列化是将字节流转换回特定语言的数据结构的逆运算。序列化和反序列化可能成为数据交换的瓶颈，因为这些操作会占用大量 CPU。高效的序列化和反序列化是 Protobuf 的另一个设计目标。最近的编码技术，例如 Protobuf 和 FlatBuffers，源自 1990 年代初期的 DCE/RPC（ 分布式计算环境/远程过程调用 Distributed Computing Environment/Remote Procedure Call ）计划。与 DCE/RPC 一样，Protobuf 在数据交换中为 IDL（接口定义语言）和编码层做出了贡献。本文将着眼于这两层，然后提供 Go 和 Java 中的代码示例以充实 Protobuf 的细节，并表明 Protobuf 是易于使用的。Protobuf 作为一个 IDL 和编码层像 Protobuf 一样，DCE/RPC 被设计为与语言和平台无关。适当的库和实用程序允许任何语言和平台用于 DCE/RPC 领域。此外，DCE/RPC 体系结构非常优雅。IDL 文档是一侧的远程过程与另一侧的调用者之间的协定。Protobuf 也是以 IDL 文档为中心的。IDL 文档是文本，在 DCE/RPC 中，使用基本 C 语法以及元数据的语法扩展（方括号）和一些新的关键字，例如 interface。这是一个例子：[uuid (2d6ead46-05e3-11ca-7dd1-426909beabcd), version(1.0)] interface echo { const long int ECHO_SIZE = 512; void echo( [in] handle_t h, [in, string] idl_char from_client[ ], [out, string] idl_char from_service[ECHO_SIZE] ); }该 IDL 文档声明了一个名为 echo 的过程，该过程带有三个参数：类型为 handle_t（实现指针）和 idl_char（ASCII 字符数组）的 [in] 参数被传递给远程过程，而 [out] 参数（也是一个字符串）从该过程中传回。在此示例中，echo 过程不会显式返回值（echo 左侧的 void），但也可以返回值。返回值，以及一个或多个 [out] 参数，允许远程过程任意返回许多值。下一节将介绍 Protobuf IDL，它的语法不同，但同样用作数据交换中的协定。DCE/RPC 和 Protobuf 中的 IDL 文档是创建用于交换数据的基础结构代码的实用程序的输入：IDL 文档 —> DCE/PRC 或 Protobuf 实用程序 —> 数据交换的支持代码作为相对简单的文本，IDL 是同样便于人类阅读的关于数据交换细节的文档（特别是交换的数据项的数量和每个项的数据类型）。Protobuf 可用于现代 RPC 系统，例如 gRPC；但是 Protobuf 本身仅提供 IDL 层和编码层，用于从发送者传递到接收者的消息。与原本的 DCE/RPC 一样，Protobuf 编码是二进制的，但效率更高。目前，XML 和 JSON 编码仍在通过 Web 服务等技术进行的数据交换中占主导地位，这些技术利用 Web 服务器、传输协议（例如 TCP、HTTP）以及标准库和实用程序等原有的基础设施来处理 XML 和 JSON 文档。 此外，各种类型的数据库系统可以存储 XML 和 JSON 文档，甚至旧式关系型系统也可以轻松生成查询结果的 XML 编码。现在，每种通用编程语言都具有支持 XML 和 JSON 的库。那么，是什么让我们回到 Protobuf 之类的二进制编码系统呢？让我们看一下负十进制值 -128。以 2 的补码二进制表示形式（在系统和语言中占主导地位）中，此值可以存储在单个 8 位字节中：10000000。此整数值在 XML 或 JSON 中的文本编码需要多个字节。例如，UTF-8 编码需要四个字节的字符串，即 -128，即每个字符一个字节（十六进制，值为 0x2d、0x31、0x32 和 0x38）。XML 和 JSON 还添加了标记字符，例如尖括号和大括号。有关 Protobuf 编码的详细信息下面就会介绍，但现在的关注点是一个通用点：文本编码的压缩性明显低于二进制编码。在 Go 中使用 Protobuf 的示例我的代码示例着重于 Protobuf 而不是 RPC。以下是第一个示例的概述：名为 dataitem.proto 的 IDL 文件定义了一个 Protobuf 消息，它具有六个不同类型的字段：具有不同范围的整数值、固定大小的浮点值以及两个不同长度的字符串。Protobuf 编译器使用 IDL 文件生成 Go 版本（以及后面的 Java 版本）的 Protobuf 消息及支持函数。Go 应用程序使用随机生成的值填充原生的 Go 数据结构，然后将结果序列化为本地文件。为了进行比较， XML 和 JSON 编码也被序列化为本地文件。作为测试，Go 应用程序通过反序列化 Protobuf 文件的内容来重建其原生数据结构的实例。作为语言中立性测试，Java 应用程序还会对 Protobuf 文件的内容进行反序列化以获取原生数据结构的实例。我的网站上提供了该 IDL 文件以及两个 Go 和一个 Java 源文件，打包为 ZIP 文件。最重要的 Protobuf IDL 文档如下所示。该文档存储在文件 dataitem.proto 中，并具有常规的.proto 扩展名。示例 1、Protobuf IDL 文档syntax = "proto3"; package main; message DataItem { int64 oddA = 1; int64 evenA = 2; int32 oddB = 3; int32 evenB = 4; float small = 5; float big = 6; string short = 7; string long = 8; }该 IDL 使用当前的 proto3 而不是较早的 proto2 语法。软件包名称（在本例中为 main）是可选的，但是惯例使用它以避免名称冲突。这个结构化的消息包含八个字段，每个字段都有一个 Protobuf 数据类型（例如，int64、string）、名称（例如，oddA、short）和一个等号 = 之后的数字标签（即键）。标签（在此示例中为 1 到 8）是唯一的整数标识符，用于确定字段序列化的顺序。Protobuf 消息可以嵌套到任意级别，而一个消息可以是另外一个消息的字段类型。这是一个使用 DataItem 消息作为字段类型的示例：message DataItems { repeated DataItem item = 1; }单个 DataItems 消息由重复的（零个或多个）DataItem 消息组成。为了清晰起见，Protobuf 还支持枚举类型：enum PartnershipStatus { reserved "FREE", "CONSTRAINED", "OTHER"; }reserved 限定符确保用于实现这三个符号名的数值不能重复使用。为了生成一个或多个声明 Protobuf 消息结构的特定于语言的版本，包含这些结构的 IDL 文件被传递到protoc 编译器（可在 Protobuf GitHub 存储库中找到）。对于 Go 代码，可以以通常的方式安装支持的 Protobuf 库（这里以 ％ 作为命令行提示符）：% go get github.com/golang/protobuf/proto将 Protobuf IDL 文件 dataitem.proto 编译为 Go 源代码的命令是：% protoc --go_out=. dataitem.proto标志 --go_out 指示编译器生成 Go 源代码。其他语言也有类似的标志。在这种情况下，结果是一个名为 dataitem.pb.go 的文件，该文件足够小，可以将其基本内容复制到 Go 应用程序中。以下是生成的代码的主要部分：var _ = proto.Marshal type DataItem struct { OddA int64 `protobuf:"varint,1,opt,name=oddA" json:"oddA,omitempty"` EvenA int64 `protobuf:"varint,2,opt,name=evenA" json:"evenA,omitempty"` OddB int32 `protobuf:"varint,3,opt,name=oddB" json:"oddB,omitempty"` EvenB int32 `protobuf:"varint,4,opt,name=evenB" json:"evenB,omitempty"` Small float32 `protobuf:"fixed32,5,opt,name=small" json:"small,omitempty"` Big float32 `protobuf:"fixed32,6,opt,name=big" json:"big,omitempty"` Short string `protobuf:"bytes,7,opt,name=short" json:"short,omitempty"` Long string `protobuf:"bytes,8,opt,name=long" json:"long,omitempty"` } func (m *DataItem) Reset() { *m = DataItem{} } func (m *DataItem) String() string { return proto.CompactTextString(m) } func (*DataItem) ProtoMessage() {} func init() {}编译器生成的代码具有 Go 结构 DataItem，该结构导出 Go 字段（名称现已大写开头），该字段与 Protobuf IDL 中声明的名称匹配。该结构字段具有标准的 Go 数据类型：int32、int64、float32 和 string。在每个字段行的末尾，是描述 Protobuf 类型的字符串，提供 Protobuf IDL 文档中的数字标签及有关 JSON 信息的元数据，这将在后面讨论。此外也有函数；最重要的是 Proto.Marshal，用于将 DataItem 结构的实例序列化为 Protobuf 格式。辅助函数包括：清除 DataItem 结构的 Reset，生成 DataItem 的单行字符串表示的 String。描述 Protobuf 编码的元数据应在更详细地分析 Go 程序之前进行仔细研究。Protobuf 编码Protobuf 消息的结构为键/值对的集合，其中数字标签为键，相应的字段为值。字段名称（例如，oddA 和 small）是供人类阅读的，但是 protoc 编译器的确使用了字段名称来生成特定于语言的对应名称。例如，Protobuf IDL 中的 oddA 和 small 名称在 Go 结构中分别成为字段 OddA 和 Small。键和它们的值都被编码，但是有一个重要的区别：一些数字值具有固定大小的 32 或 64 位的编码，而其他数字（包括消息标签）则是 varint 编码的，位数取决于整数的绝对值。例如，整数值 1 到 15 需要 8 位 varint 编码，而值 16 到 2047 需要 16 位。varint 编码在本质上与 UTF-8 编码类似（但细节不同），它偏爱较小的整数值而不是较大的整数值。（有关详细分析，请参见 Protobuf 编码指南）结果是，Protobuf 消息应该在字段中具有较小的整数值（如果可能），并且键数应尽可能少，但每个字段至少得有一个键。下表 1 列出了 Protobuf 编码的要点：编码示例类型长度varintint32、uint32、int64可变长度fixedfixed32、float、double固定的 32 位或 64 位长度字节序列string、bytes序列长度表 1. Protobuf 数据类型未明确固定长度的整数类型是 varint 编码的；因此，在 varint 类型中，例如 uint32（u 代表无符号），数字 32 描述了整数的范围（在这种情况下为 0 到 2 32 - 1），而不是其位的大小，该位大小取决于值。相比之下，对于固定长度类型（例如 fixed32 或 double），Protobuf 编码分别需要 32 位和 64 位。Protobuf 中的字符串是字节序列；因此，字段编码的大小就是字节序列的长度。另一个高效的方法值得一提。回想一下前面的示例，其中的 DataItems 消息由重复的 DataItem 实例组成：message DataItems { repeated DataItem item = 1; }repeated 表示 DataItem 实例是打包的：集合具有单个标签，在这里是 1。因此，具有重复的 DataItem 实例的 DataItems 消息比具有多个但单独的 DataItem 字段、每个字段都需要自己的标签的消息的效率更高。了解了这一背景，让我们回到 Go 程序。dataItem 程序的细节dataItem 程序创建一个 DataItem 实例，并使用适当类型的随机生成的值填充字段。Go 有一个 rand 包，带有用于生成伪随机整数和浮点值的函数，而我的 randString 函数可以从字符集中生成指定长度的伪随机字符串。设计目标是要有一个具有不同类型和位大小的字段值的 DataItem 实例。例如，OddA 和 EvenA 值分别是 64 位非负整数值的奇数和偶数；但是 OddB 和 EvenB 变体的大小为 32 位，并存放 0 到 2047 之间的小整数值。随机浮点值的大小为 32 位，字符串为 16（Short）和 32（Long）字符的长度。这是用随机值填充 DataItem 结构的代码段：// 可变长度整数 n1 := rand.Int63() // 大整数 if (n1 & 1) == 0 { n1++ } // 确保其是奇数 ... n3 := rand.Int31() % UpperBound // 小整数 if (n3 & 1) == 0 { n3++ } // 确保其是奇数 // 固定长度浮点数 ... t1 := rand.Float32() t2 := rand.Float32() ... // 字符串 str1 := randString(StrShort) str2 := randString(StrLong) // 消息 dataItem := &DataItem { OddA: n1, EvenA: n2, OddB: n3, EvenB: n4, Big: f1, Small: f2, Short: str1, Long: str2, }创建并填充值后，DataItem 实例将以 XML、JSON 和 Protobuf 进行编码，每种编码均写入本地文件：func encodeAndserialize(dataItem *DataItem) { bytes, _ := xml.MarshalIndent(dataItem, "", " ") // Xml to dataitem.xml ioutil.WriteFile(XmlFile, bytes, 0644) // 0644 is file access permissions bytes, _ = json.MarshalIndent(dataItem, "", " ") // Json to dataitem.json ioutil.WriteFile(JsonFile, bytes, 0644) bytes, _ = proto.Marshal(dataItem) // Protobuf to dataitem.pbuf ioutil.WriteFile(PbufFile, bytes, 0644) }这三个序列化函数使用术语 marshal，它与 serialize 意思大致相同。如代码所示，三个 Marshal 函数均返回一个字节数组，然后将其写入文件。（为简单起见，忽略可能的错误处理。）在示例运行中，文件大小为：dataitem.xml: 262 bytes dataitem.json: 212 bytes dataitem.pbuf: 88 bytesProtobuf 编码明显小于其他两个编码方案。通过消除缩进字符（在这种情况下为空白和换行符），可以稍微减小 XML 和 JSON 序列化的大小。以下是 dataitem.json 文件，该文件最终是由 json.MarshalIndent 调用产生的，并添加了以 ## 开头的注释：{ "oddA": 4744002665212642479, ## 64-bit >= 0 "evenA": 2395006495604861128, ## ditto "oddB": 57, ## 32-bit >= 0 but < 2048 "evenB": 468, ## ditto "small": 0.7562016, ## 32-bit floating-point "big": 0.85202795, ## ditto "short": "ClH1oDaTtoX$HBN5", ## 16 random chars "long": "xId0rD3Cri%3Wt%^QjcFLJgyXBu9^DZI" ## 32 random chars }尽管这些序列化的数据写入到本地文件中，但是也可以使用相同的方法将数据写入网络连接的输出流。测试序列化和反序列化Go 程序接下来通过将先前写入 dataitem.pbuf 文件的字节反序列化为 DataItem 实例来运行基本测试。这是代码段，其中去除了错误检查部分：filebytes, err := ioutil.ReadFile(PbufFile) // get the bytes from the file ... testItem.Reset() // clear the DataItem structure err = proto.Unmarshal(filebytes, testItem) // deserialize into a DataItem instance用于 Protbuf 反序列化的 proto.Unmarshal 函数与 proto.Marshal 函数相反。原始的 DataItem 和反序列化的副本将被打印出来以确认完全匹配：Original: 2041519981506242154 3041486079683013705 1192 1879 0.572123 0.326855 boPb#T0O8Xd&Ps5EnSZqDg4Qztvo7IIs 9vH66AiGSQgCDxk& Deserialized: 2041519981506242154 3041486079683013705 1192 1879 0.572123 0.326855 boPb#T0O8Xd&Ps5EnSZqDg4Qztvo7IIs 9vH66AiGSQgCDxk&一个 Java Protobuf 客户端用 Java 写的示例是为了确认 Protobuf 的语言中立性。原始 IDL 文件可用于生成 Java 支持代码，其中涉及嵌套类。但是，为了抑制警告信息，可以进行一些补充。这是修订版，它指定了一个 DataMsg 作为外部类的名称，内部类在该 Protobuf 消息后面自动命名为 DataItem：syntax = "proto3"; package main; option java_outer_classname = "DataMsg"; message DataItem { ...进行此更改后，protoc 编译与以前相同，只是所期望的输出现在是 Java 而不是 Go：% protoc --java_out=. dataitem.proto生成的源文件（在名为 main 的子目录中）为 DataMsg.java，长度约为 1,120 行：Java 并不简洁。编译然后运行 Java 代码需要具有 Protobuf 库支持的 JAR 文件。该文件位于 Maven 存储库中。放置好这些片段后，我的测试代码相对较短（并且在 ZIP 文件中以 Main.java 形式提供）：package main; import java.io.FileInputStream; public class Main { public static void main(String[] args) { String path = "dataitem.pbuf"; // from the Go program's serialization try { DataMsg.DataItem deserial = DataMsg.DataItem.newBuilder().mergeFrom(new FileInputStream(path)).build(); System.out.println(deserial.getOddA()); // 64-bit odd System.out.println(deserial.getLong()); // 32-character string } catch(Exception e) { System.err.println(e); } } }当然，生产级的测试将更加彻底，但是即使是该初步测试也可以证明 Protobuf 的语言中立性：dataitem.pbuf 文件是 Go 程序对 Go 语言版的 DataItem 进行序列化的结果，并且该文件中的字节被反序列化以产生一个 Java 语言的 DataItem 实例。Java 测试的输出与 Go 测试的输出相同。用 numPairs 程序来结束让我们以一个示例作为结尾，来突出 Protobuf 效率，但又强调在任何编码技术中都会涉及到的成本。考虑以下 Protobuf IDL 文件：syntax = "proto3"; package main; message NumPairs { repeated NumPair pair = 1; } message NumPair { int32 odd = 1; int32 even = 2; }NumPair 消息由两个 int32 值以及每个字段的整数标签组成。NumPairs 消息是嵌入的 NumPair 消息的序列。Go 语言的 numPairs 程序（如下）创建了 200 万个 NumPair 实例，每个实例都附加到 NumPairs 消息中。该消息可以按常规方式进行序列化和反序列化。示例 2、numPairs 程序package main import ( "math/rand" "time" "encoding/xml" "encoding/json" "io/ioutil" "github.com/golang/protobuf/proto" ) // protoc-generated code: start var _ = proto.Marshal type NumPairs struct { Pair []*NumPair `protobuf:"bytes,1,rep,name=pair" json:"pair,omitempty"` } func (m *NumPairs) Reset() { *m = NumPairs{} } func (m *NumPairs) String() string { return proto.CompactTextString(m) } func (*NumPairs) ProtoMessage() {} func (m *NumPairs) GetPair() []*NumPair { if m != nil { return m.Pair } return nil } type NumPair struct { Odd int32 `protobuf:"varint,1,opt,name=odd" json:"odd,omitempty"` Even int32 `protobuf:"varint,2,opt,name=even" json:"even,omitempty"` } func (m *NumPair) Reset() { *m = NumPair{} } func (m *NumPair) String() string { return proto.CompactTextString(m) } func (*NumPair) ProtoMessage() {} func init() {} // protoc-generated code: finish var numPairsStruct NumPairs var numPairs = &numPairsStruct func encodeAndserialize() { // XML encoding filename := "./pairs.xml" bytes, _ := xml.MarshalIndent(numPairs, "", " ") ioutil.WriteFile(filename, bytes, 0644) // JSON encoding filename = "./pairs.json" bytes, _ = json.MarshalIndent(numPairs, "", " ") ioutil.WriteFile(filename, bytes, 0644) // ProtoBuf encoding filename = "./pairs.pbuf" bytes, _ = proto.Marshal(numPairs) ioutil.WriteFile(filename, bytes, 0644) } const HowMany = 200 * 100 * 100 // two million func main() { rand.Seed(time.Now().UnixNano()) // uncomment the modulus operations to get the more efficient version for i := 0; i < HowMany; i++ { n1 := rand.Int31() // % 2047 if (n1 & 1) == 0 { n1++ } // ensure it's odd n2 := rand.Int31() // % 2047 if (n2 & 1) == 1 { n2++ } // ensure it's even next := &NumPair { Odd: n1, Even: n2, } numPairs.Pair = append(numPairs.Pair, next) } encodeAndserialize() }每个 NumPair 中随机生成的奇数和偶数值的范围在 0 到 20 亿之间变化。就原始数据（而非编码数据）而言，Go 程序中生成的整数总共为 16MB：每个 NumPair 为两个整数，总计为 400 万个整数，每个值的大小为四个字节。为了进行比较，下表列出了 XML、JSON 和 Protobuf 编码的示例 NumsPairs 消息的 200 万个 NumPair 实例。原始数据也包括在内。由于 numPairs 程序生成随机值，因此样本运行的输出有所不同，但接近表中显示的大小。编码文件字节大小Pbuf/其它 比例无pairs.raw16MB169%Protobufpairs.pbuf27MB—JSONpairs.json100MB27%XMLpairs.xml126MB21%表 2. 16MB 整数的编码开销不出所料，Protobuf 和之后的 XML 和 JSON 差别明显。Protobuf 编码大约是 JSON 的四分之一，是 XML 的五分之一。但是原始数据清楚地表明 Protobuf 也会产生编码开销：序列化的 Protobuf 消息比原始数据大 11MB。包括 Protobuf 在内的任何编码都涉及结构化数据，这不可避免地会增加字节。序列化的 200 万个 NumPair 实例中的每个实例都包含四个整数值：Go 结构中的 Even 和 Odd 字段分别一个，而 Protobuf 编码中的每个字段、每个标签一个。对于原始数据（而不是编码数据），每个实例将达到 16 个字节，样本 NumPairs 消息中有 200 万个实例。但是 Protobuf 标记（如 NumPair 字段中的 int32 值）使用 varint 编码，因此字节长度有所不同。特别是，小的整数值（在这种情况下，包括标签在内）需要不到四个字节进行编码。如果对 numPairs 程序进行了修改，以使两个 NumPair 字段的值小于 2048，且其编码为一或两个字节，则 Protobuf 编码将从 27MB 下降到 16MB，这正是原始数据的大小。下表总结了样本运行中的新编码大小。编码文件字节大小Pbuf/其它 比例Nonepairs.raw16MB100%Protobufpairs.pbuf16MB—JSONpairs.json77MB21%XMLpairs.xml103MB15%表 3. 编码 16MB 的小于 2048 的整数总之，修改后的 numPairs 程序的字段值小于 2048，可减少原始数据中每个四字节整数值的大小。但是 Protobuf 编码仍然需要标签，这些标签会在 Protobuf 消息中添加字节。Protobuf 编码确实会增加消息大小，但是如果要编码相对较小的整数值（无论是字段还是键），则可以通过 varint 因子来减少此开销。对于包含混合类型的结构化数据（且整数值相对较小）的中等大小的消息，Protobuf 明显优于 XML 和 JSON 等选项。在其他情况下，数据可能不适合 Protobuf 编码。例如，如果两个应用程序需要共享大量文本记录或大整数值，则可以采用压缩而不是编码技术。via: https://opensource.com/article/19/10/protobuf-data-interchange作者：Marty Kalin 选题：lujun9972 译者：wxy 校对：wxy本文由 LCTT 原创编译，Linux中国 荣誉推出
如何用 Linux 命令行工具解析和格式化输出 JSON
JSON 是一种轻量级且与语言无关的数据存储格式，易于与大多数编程语言集成，也易于人类理解 —— 当然，如果格式正确的话。JSON 这个词代表 Java Script Object Notation，虽然它以 JavaScript 开头，而且主要用于在服务器和浏览器之间交换数据，但现在正在用于许多领域，包括嵌入式系统。在这里，我们将使用 Linux 上的命令行工具解析并格式化打印 JSON。它对于在 shell 脚本中处理大型 JSON 数据或在 shell 脚本中处理 JSON 数据非常有用。什么是格式化输出？JSON 数据的结构更具人性化。但是在大多数情况下，JSON 数据会存储在一行中，甚至没有行结束字符。显然，这对于手动阅读和编辑不太方便。这是 格式化输出 pretty print 就很有用。这个该名称不言自明：重新格式化 JSON 文本，使人们读起来更清晰。这被称为 JSON 格式化输出。用 Linux 命令行工具解析和格式化输出 JSON可以使用命令行文本处理器解析 JSON 数据，例如 awk、sed 和 gerp。实际上 JSON.awk 是一个来做这个的 awk 脚本。但是，也有一些专用工具可用于同一目的。jq 或 jshon，shell 下的 JSON 解析器，它们都非常有用。Shell 脚本，如 JSON.sh 或 jsonv.sh，用于在 bash、zsh 或 dash shell 中解析JSON。JSON.awk，JSON 解析器 awk 脚本。像 json.tool 这样的 Python 模块。undercore-cli，基于 Node.js 和 javascript。在本教程中，我只关注 jq，这是一个 shell 下的非常强大的 JSON 解析器，具有高级过滤和脚本编程功能。JSON 格式化输出JSON 数据可能放在一行上使人难以解读，因此为了使其具有一定的可读性，JSON 格式化输出就可用于此目的的。示例：来自 jsonip.com 的数据，使用 curl 或 wget 工具获得 JSON 格式的外部 IP 地址，如下所示。$ wget -cq http://jsonip.com/ -O -实际数据看起来类似这样：{"ip":"111.222.333.444","about":"/about","Pro!":"http://getjsonip.com"}现在使用 jq 格式化输出它：$ wget -cq http://jsonip.com/ -O - | jq '.'通过 jq 过滤了该结果之后，它应该看起来类似这样：{ "ip": "111.222.333.444", "about": "/about", "Pro!": "http://getjsonip.com" }同样也可以通过 Python json.tool 模块做到。示例如下：$ cat anything.json | python -m json.tool这种基于 Python 的解决方案对于大多数用户来说应该没问题，但是如果没有预安装或无法安装 Python 则不行，比如在嵌入式系统上。然而，json.tool Python 模块具有明显的优势，它是跨平台的。因此，你可以在 Windows、Linux 或 Mac OS 上无缝使用它。如何用 jq 解析 JSON首先，你需要安装 jq，它已被大多数 GNU/Linux 发行版选中，并使用各自的软件包安装程序命令进行安装。在 Arch Linux 上：$ sudo pacman -S jq在 Debian、Ubuntu、Linux Mint 上：$ sudo apt-get install jq在 Fedora 上：$ sudo dnf install jq在 openSUSE 上：$ sudo zypper install jq对于其它操作系统或平台参见官方的安装指导。jq 的基本过滤和标识符功能jq 可以从 STDIN 或文件中读取 JSON 数据。你可以根据情况使用。单个符号 . 是最基本的过滤器。这些过滤器也称为对象标识符-索引。jq 使用单个 . 过滤器基本上相当将输入的 JSON 文件格式化输出。单引号：不必始终使用单引号。但是如果你在一行中组合几个过滤器，那么你必须使用它们。双引号：你必须用两个双引号括起任何特殊字符，如 @、＃、$，例如 jq .foo.”@bar”。原始数据打印：不管出于任何原因，如果你只需要最终解析的数据（不包含在双引号内），请使用带有 -r 标志的 jq 命令，如下所示：jq -r .foo.bar。解析特定数据要过滤出 JSON 的特定部分，你需要了解格式化输出的 JSON 文件的数据层次结构。来自维基百科的 JSON 数据示例：{ "firstName": "John", "lastName": "Smith", "age": 25, "address": { "streetAddress": "21 2nd Street", "city": "New York", "state": "NY", "postalCode": "10021" }, "phoneNumber": [ { "type": "home", "number": "212 555-1234" }, { "type": "fax", "number": "646 555-4567" } ], "gender": { "type": "male" } }我将在本教程中将此 JSON 数据用作示例，将其保存为 sample.json。假设我想从 sample.json 文件中过滤出地址。所以命令应该是这样的：$ jq .address sample.json示例输出：{ "streetAddress": "21 2nd Street", "city": "New York", "state": "NY", "postalCode": "10021" }再次，我想要邮政编码，然后我要添加另一个对象标识符-索引，即另一个过滤器。$ cat sample.json | jq .address.postalCode另请注意，过滤器区分大小写，并且你必须使用完全相同的字符串来获取有意义的输出，否则就是 null。从 JSON 数组中解析元素JSON 数组的元素包含在方括号内，这无疑是非常通用的。要解析数组中的元素，你必须使用 [] 标识符以及其他对象标识符索引。在此示例 JSON 数据中，电话号码存储在数组中，要从此数组中获取所有内容，你只需使用括号，像这个示例：$ jq .phoneNumber[] sample.json假设你只想要数组的第一个元素，然后使用从 0 开始的数组对象编号，对于第一个项目，使用 [0]，对于下一个项目，它应该每步增加 1。$ jq .phoneNumber[0] sample.json脚本编程示例假设我只想要家庭电话，而不是整个 JSON 数组数据。这就是用 jq 命令脚本编写的方便之处。$ cat sample.json | jq -r '.phoneNumber[] | select(.type == "home") | .number'首先，我将一个过滤器的结果传递给另一个，然后使用 select 属性选择特定类型的数据，再次将结果传递给另一个过滤器。解释每种类型的 jq 过滤器和脚本编程超出了本教程的范围和目的。强烈建议你阅读 jq 手册，以便更好地理解下面的内容。资源：https://stedolan.github.io/jq/manual/http://www.compciv.org/recipes/cli/jq-for-parsing-json/https://lzone.de/cheat-sheet/jqvia: https://www.ostechnix.com/how-to-parse-and-pretty-print-json-with-linux-commandline-tools/作者：ostechnix 选题：lujun9972 译者：wxy 校对：wxy本文由 LCTT 原创编译，Linux中国 荣誉推出
如果你不希望从头开始创造一种数据格式来存放数据，JSON 是一个很好的选择。如果你对 Python 有所了解，就更加事半功倍了。下面就来介绍一下如何使用 Python 处理 JSON 数据。JSON 的全称是 JavaScript 对象表示法 JavaScript Object Notation 。这是一种以键值对的形式存储数据的格式，并且很容易解析，因而成为了一种被广泛使用的数据格式。另外，不要因为 JSON 名称而望文生义，JSON 并不仅仅在 JavaScript 中使用，它也可以在其它语言中使用。下文会介绍它是如何在 Python 中使用的。首先我们给出一个 JSON 示例：{ "name":"tux", "health":"23", "level":"4" }上面是一个和编程语言无关的原生 JSON 数据。熟悉 Python 的人会看出来这个 JSON 数据跟 Python 中的 字典 dictionary 长得很像。而这两者之间确实非常相似，如果你对 Python 中的列表和字典数据结构有一定的理解，那么 JSON 理解起来也不难。使用字典存放数据如果你的应用需要存储一些结构复杂的数据，不妨考虑使用 JSON 格式。对比你可能曾经用过的自定义格式的文本配置文件，JSON 提供了更加结构化的可递归的存储格式。同时，Python 自带的 json 模块已经提供了可以将 JSON 数据导入/导出应用时所需的所有解析库。因此，你不需要针对 JSON 自行编写代码进行解析，而其他开发人员在与你的应用进行数据交互的时候也不需要去解析新的数据格式。正是这个原因，JSON 在数据交换时被广泛地采用了。以下是一段在 Python 中使用嵌套字典的代码：#!/usr/bin/env python3 import json # instantiate an empty dict team = {} # add a team member team['tux'] = {'health': 23, 'level': 4} team['beastie'] = {'health': 13, 'level': 6} team['konqi'] = {'health': 18, 'level': 7}这段代码声明了一个名为 team 的字典，并初始化为一个空字典。如果要给这个字典添加内容，首先需要创建一个键，例如上面示例中的 tux、beastie、konqi，然后为这些键一一提供对应的值。上面示例中的值由一个个包含游戏玩家信息的字典充当。字典是一种可变的变量。字典中的数据可以随时添加、删除或更新。这样的特性使得字典成为了应用程序存储数据的极好选择。使用 JSON 格式存储数据如果存放在字典中的数据需要持久存储，这些数据就需要写到文件当中。这个时候就需要用到 Python 中的 json 模块了：with open('mydata.json', 'w') as f: json.dump(team, f)上面的代码首先创建了一个名为 mydata.json 的文件，然后以写模式打开了这个文件，这个被打开的文件以变量 f 表示（当然也可以用任何你喜欢的名称，例如 file、output 等）。而 json 模块中的 dump() 方法则是用于将一个字典输出到一个文件中。从应用中导出数据就是这么简单，同时这些导出的数据是结构化的、可理解的。现在可以查看导出的数据：$ cat mydata.json {"tux": {"health": 23, "level": 4}, "beastie": {"health": 13, "level": 6}, "konqi": {"health": 18, "level": 7}}从 JSON 文件中读取数据如果已经将数据以 JSON 格式导出到文件中了，也有可能需要将这些数据读回到应用中去。这个时候，可以使用 Python json 模块中的 load() 方法：#!/usr/bin/env python3 import json f = open('mydata.json') team = json.load(f) print(team['tux']) print(team['tux']['health']) print(team['tux']['level']) print(team['beastie']) print(team['beastie']['health']) print(team['beastie']['level']) # when finished, close the file f.close()这个方法实现了和保存文件大致相反的操作。使用一个变量 f 来表示打开了的文件，然后使用 json 模块中的 load() 方法读取文件中的数据并存放到 team 变量中。其中的 print() 展示了如何查看读取到的数据。在过于复杂的字典中迭代调用字典键的时候有可能会稍微转不过弯来，但只要熟悉整个数据的结构，就可以慢慢摸索出其中的逻辑。当然，这里使用 print() 的方式太不灵活了。你可以将其改写成使用 for 循环的形式：for i in team.values(): print(i)使用 JSON如上所述，在 Python 中可以很轻松地处理 JSON 数据。因此只要你的数据符合 JSON 的模式，就可以选择使用 JSON。JSON 非常灵活易用，下次使用 Python 的时候不妨尝试一下。via: https://opensource.com/article/19/7/save-and-load-data-python-json作者：Seth Kenlon 选题：lujun9972 译者：HankChow 校对：wxy本文由 LCTT 原创编译，Linux中国 荣誉推出
一段超级严肃的关于样本序列化的集合、子集和超集的文字我是一名开发者，我读代码，我写代码，我写会写代码的代码，我写会写出供其它代码读的代码的代码。这些都非常火星语，但是有其美妙之处。然而，最后一点，写会写出供其它代码读的代码的代码，可以很快变得比这段文字更费解。有很多方法可以做到这一点。一种不那么复杂而且开发者社区最爱的方式是数据序列化。对于那些不了解我刚刚抛给你的时髦词的人，数据序列化是从一个系统获取一些信息，将其转换为其它系统可以读取的格式，然后将其传递给其它系统的过程。虽然数据序列化格式多到可以埋葬哈利法塔，但它们大多分为两类：易于人类读写，易于机器读写。很难两全其美，因为人类喜欢让我们更具表现力的松散类型和灵活格式标准，而机器倾向于被确切告知一切事情而没有二义性和细节缺失，并且认为“严格规范”才是它们最爱的口味。由于我是一名 web 开发者，而且我们是一个创建网站的机构，我们将坚持使用 web 系统可以理解或不需要太多努力就能理解的特殊格式，而且对人类可读性特别有用的格式：XML、JSON、TOML、CSON 以及 YAML。每个都有各自的优缺点和适当的用例场景。事实最先回到互联网的早期，一些非常聪明的家伙决定整合一种让每个系统都能理解的标准语言，并创造性地将其命名为 标准通用标记语言 Standard Generalized Markup Language （简称 SGML）。SGML 非常灵活，发布者也很好地定义了它。它成为了 XML、SVG 和 HTML 等语言之父。所有这三个都符合 SGML 规范，可是它们都是规则更严格、灵活性更少的子集。最终，人们开始看到非常小、简洁、易读且易于生成的数据的好处，这些数据可以在系统之间以编程的方式共享，而开销很小。大约在那个时候，JSON 诞生了并且能够满足所有的需求。而另一方面，其它语言也开始出现以处理更多的专业用例，如 CSON，TOML 和 YAML。XML：不行了原本，XML 语言非常灵活且易于编写，但它的缺点是冗长，人类难以阅读、计算机非常难以读取，并且有很多语法对于传达信息并不是完全必要的。今天，它在 web 上的数据序列化的用途已经消失了。除非你在编写 HTML 或者 SVG，否则你不太能在许多其它地方看到 XML。一些过时的系统今天仍在使用它，但是用它传递数据往往太重了。我已经可以听到 XML 老爷爷开始在它们的石碑上乱写为什么 XML 是了不起的，所以我将提供一个小小的补充：XML 可以很容易地由系统和人读写。然而，真的，我的意思是荒谬的，很难创建一个可以规范的读取它的系统。这是一个简单美观的 XML 示例：<book id="bk101"> <author>Gambardella, Matthew</author> XML Developer's Guide <genre>Computer</genre> <price>44.95</price> <publish_date>2000-10-01</publish_date> <description>An in-depth look at creating applications with XML.</description> </book>太棒了。易于阅读、理解、写入，也容易编码一个可以读写它的系统。但请考虑这个例子：<!DOCTYPE r [ <!ENTITY y "a]>b"> ]> <r> " /> [a>b b <a <?x <!-- ?> c --> d </r>这上面是 100% 有效的 XML。几乎不可能阅读、理解或推理。编写可以使用和理解这个的代码将花费至少 36 根头发和 248 磅咖啡渣。我们没有那么多时间或咖啡，而且我们大多数老程序员们现在都是秃头。所以，让它活在我们的记忆里，就像 css hacks、IE 6 浏览器 和真空管一样好了。JSON：并列聚会好吧，我们都同意，XML = 差劲。那么，好的替代品是什么？ JavaScript 对象表示法 JavaScript Object Notation ，简称 JSON。JSON（读起来像 Jason 这个名字） 是 Brendan Eich 发明的，并且得到了伟大而强力的 JavaScript 意见领袖 Douglas Crockford 的推广。它现在几乎用在任何地方。这种格式很容易由人和机器编写，按规范中的严格规则解析也相当容易，并且灵活 —— 允许深层嵌套数据，支持所有的原始数据类型，及将集合解释为数组或对象。JSON 成为了将数据从一个系统传输到另一个系统的事实标准。几乎所有语言都有内置读写它的功能。JSON语法很简单。方括号表示数组，花括号表示记录，由冒号分隔的两个值分别表示属性或“键”（在左边）、值（在右边）。所有键必须用双引号括起来： { "books": [ { "id": "bk102", "author": "Crockford, Douglas", "title": "JavaScript: The Good Parts", "genre": "Computer", "price": 29.99, "publish_date": "2008-05-01", "description": "Unearthing the Excellence in JavaScript" } ] }这对你来说应该是完全有意义的。它简洁明了，并且从 XML 中删除了大量额外废话，并传达相同数量的信息。JSON 现在是王道，本文剩下的部分会介绍其它语言格式，这些格式只不过是 JSON 的简化版，尝试让其更简洁或对人类更易读，可结构还是非常相似的。TOML: 缩短到彻底的利他主义TOML（ Tom 的显而易见的最小化语言 Tom’s Obvious, Minimal Language ）允许以相当快捷、简洁的方式定义深层嵌套的数据结构。名字中的 Tom 是指发明者 Tom Preston Werner，他是一位活跃于我们行业的创造者和软件开发人员。与 JSON 相比，语法有点尴尬，更类似 ini 文件。这不是一个糟糕的语法，但是需要一些时间适应。books id = 'bk101' author = 'Crockford, Douglas' title = 'JavaScript: The Good Parts' genre = 'Computer' price = 29.99 publish_date = 2008-05-01T00:00:00+00:00 description = 'Unearthing the Excellence in JavaScript'TOML 中集成了一些很棒的功能，例如多行字符串、保留字符的自动转义、日期、时间、整数、浮点数、科学记数法和“表扩展”等数据类型。最后一点是特别的，是 TOML 如此简洁的原因：[a.b.c] d = 'Hello' e = 'World'以上扩展到以下内容：{ "a": { "b": { "c": { "d": "Hello" "e": "World" } } } }使用 TOML，你可以肯定在时间和文件长度上会节省不少。很少有系统使用它或非常类似的东西作为配置，这是它最大的缺点。根本没有很多语言或库可以用来解释 TOML。CSON: 特定系统所包含的简单样本首先，有两个 CSON 规范。 一个代表 CoffeeScript Object Notation，另一个代表 Cursive Script Object Notation。后者不经常使用，所以我们不会关注它。我们只关注 CoffeeScript。CSON 需要一点介绍。首先，我们来谈谈 CoffeeScript。CoffeeScript 是一种通过运行编译器生成 JavaScript 的语言。它允许你以更加简洁的语法编写 JavaScript 并转译成实际的 JavaScript，然后你可以在你的 web 应用程序中使用它。CoffeeScript 通过删除 JavaScript 中必需的许多额外语法，使编写 JavaScript 变得更容易。CoffeeScript 摆脱的一个大问题是花括号 —— 不需要它们。同样，CSON 是没有大括号的 JSON。它依赖于缩进来确定数据的层次结构。CSON 非常易于读写，并且通常比 JSON 需要更少的代码行，因为没有括号。CSON 还提供一些 JSON 不提供的额外细节。多行字符串非常容易编写，你可以通过使用 # 符号开始一行来输入注释)，并且不需要用逗号分隔键值对。books: [ id: 'bk102' author: 'Crockford, Douglas' title: 'JavaScript: The Good Parts' genre: 'Computer' price: 29.99 publish_date: '2008-05-01' description: 'Unearthing the Excellence in JavaScript' ]这是 CSON 的大问题。它是 CoffeScript 对象表示法 CoffeeScript Object Notation 。也就是说你要用 CoffeeScript 解析/标记化/lex/转译或其它方式来使用 CSON。CoffeeScript 是读取数据的系统。如果数据序列化的目的是允许数据从一个系统传递到另一个系统，这里我们有一个只能由单个系统读取的数据序列化格式，这使得它与防火火柴、防水海绵或者叉匙恼人的脆弱叉子部分一样有用。如果这种格式被其它系统也采用，那它在开发者世界中可能非常有用。但到目前为止这基本上没有发生，所以在 PHP 或 JAVA 等替代语言中使用它是不行的。YAML：年轻人的呼喊开发人员感到高兴，因为 YAML 来自一个 Python 的贡献者。YAML 具有与 CSON 相同的功能集和类似的语法，有一系列新功能，以及几乎所有 web 编程语言都可用的解析器。它还有一些额外的功能，如循环引用、软包装、多行键、类型转换标签、二进制数据、对象合并和集合映射。它具有非常好的可读性和可写性，并且是 JSON 的超集，因此你可以在 YAML 中使用完全合格的 JSON 语法并且一切正常工作。你几乎不需要引号，它可以解释大多数基本数据类型（字符串、整数、浮点数、布尔值等）。books: - id: bk102 author: Crockford, Douglas title: 'JavaScript: The Good Parts' genre: Computer price: 29.99 publish_date: !!str 2008-05-01 description: Unearthing the Excellence in JavaScript业界的年轻人正在迅速采用 YAML 作为他们首选的数据序列化和系统配置格式。他们这样做很机智。YAML 具有像 CSON 一样简洁的所有好处，以及与 JSON 一样的数据类型解释的所有功能。YAML 像加拿大人容易相处一样容易阅读。YAML 有两个问题，对我而言，第一个是大问题。在撰写本文时，YAML 解析器尚未内置于多种语言，因此你需要使用第三方库或扩展来为你选择的语言解析 .yaml 文件。这不是什么大问题，可似乎大多数为 YAML 创建解析器的开发人员都选择随机将“附加功能”放入解析器中。有些允许标记化，有些允许链引用，有些甚至允许内联计算。这一切都很好（某种意义上），只是这些功能都不是规范的一部分，因此很难在其他语言的其他解析器中找到。这导致系统限定，你最终遇到了与 CSON 相同的问题。如果你使用仅在一个解析器中找到的功能，则其他解析器将无法解释输入。大多数这些功能都是无意义的，不属于数据集，而是属于你的应用程序逻辑，因此最好简单地忽略它们和编写符合规范的 YAML。第二个问题是很少有解析器完全实现规范。所有的基本要素都有，但是很难找到一些更复杂和更新的东西，比如软包装、文档标记和首选语言的循环引用。我还没有看到对这些东西的刚需，所以希望它们不让你很失望。考虑到上述情况，我倾向于保持 1.1 规范 中呈现的更成熟的功能集，而避免在 1.2 规范 中找到的新东西。然而，编程是一个不断发展的怪兽，所以当你读完这篇文章时，你或许就可以使用 1.2 规范了。最终哲学这是最后一段话。每个序列化语言都应该以个案标准的方式评价。当涉及机器的可读性时，有些 无出其右 the bee’s knees 。对于人类可读性，有些 名至实归 the cat’s meow ，有些只是 金玉其外 gilded turds 。以下是最终细分：如果你要编写供其他代码阅读的代码，请使用 YAML。如果你正在编写能写出供其他代码读取的代码的代码，请使用 JSON。最后，如果你正在编写将代码转译为供其他代码读取的代码的代码，请重新考虑你的人生选择。via: https://www.zionandzion.com/json-vs-xml-vs-toml-vs-cson-vs-yaml/作者：Tim Anderson 选题：lujun9972 译者：GraveAccent 校对：wxy本文由 LCTT 原创编译，Linux中国 荣誉推出
简介XML 和 JSON 是现今互联网中最常用的两种数据交换格式。XML 格式由 W3C 于 1996 年提出。JSON 格式由 Douglas Crockford 于 2002 年提出。虽然这两种格式的设计目标并不相同，但它们常常用于同一个任务，也就是数据交换中。XML 和 JSON 的文档都很完善（RFC 7159、RFC 4825），且都同时具有 人类可读性 human-readable 和 机器可读性 machine-readable 。这两种格式并没有哪一个比另一个更强，只是各自适用的领域不用。（LCTT 译注：W3C 是互联网联盟，制定了各种 Web 相关的标准，如 HTML、CSS 等。Douglas Crockford 除了制定了 JSON 格式，还致力于改进 JavaScript，开发了 JavaScript 相关工具 JSLint 和 JSMin）XML 的优点XML 与 JSON 相比有很多优点。二者间最大的不同在于 XML 可以通过在标签中添加属性这一简单的方法来存储 元数据 metadata 。而使用 JSON 时需要创建一个对象，把元数据当作对象的成员来存储。虽然二者都能达到存储元数据的目的，但在这一情况下 XML 往往是更好的选择，因为 JSON 的表达形式会让客户端程序开发人员误以为要将数据转换成一个对象。举个例子，如果你的 C++ 程序需要使用 JSON 格式发送一个附带元数据的整型数据，需要创建一个对象，用对象中的一个 名称/值对 name/value pair 来记录整型数据的值，再为每一个附带的属性添加一个名称/值对。接收到这个 JSON 的程序在读取后很可能把它当成一个对象，可事实并不是这样。虽然这是使用 JSON 传递元数据的一种变通方法，但他违背了 JSON 的核心理念：“ JSON 的结构与常规的程序语言中的结构相对应，而无需修改。 JSON’s structures look like conventional programming language structures. No restructuring is necessary. ” 1 虽然稍后我会说这也是 XML 的一个缺点，但 XML 中对命名冲突、 前缀 prefix 的处理机制赋予了它 JSON 所不具备的能力。程序员们可以通过前缀来把统一名称给予两个不同的实体。 2 当不同的实体在客户端中使用的名称相同时，这一特性会非常有用。XML 的另一个优势在于大多数的浏览器可以把它以 具有高可读性和强组织性的方式 highly readable and organized way 展现给用户。XML 的树形结构让它易于结构化，浏览器也让用户可以自行展开或折叠树中的元素，这简直就是调试的福音。XML 对比 JSON 有一个很重要的优势就是它可以记录 混合内容 mixed content 。例如在 XML 中处理包含结构化标记的字符串时，程序员们只要把带有标记的文本放在一个标签内就可以了。可因为 JSON 只包含数据，没有用于指明标签的简单方式，虽然可以使用处理元数据的解决方法，但这总有点滥用之嫌。JSON 的优点JSON 自身也有很多优点。其中最显而易见的一点就是 JSON 比 XML 简洁得多。因为 XML 中需要打开和关闭标签，而 JSON 使用名称/值对表示数据，使用简单的 { 和 } 来标记对象，[ 和 ] 来标记数组，, 来表示数据的分隔，: 表示名称和值的分隔。就算是使用 gzip 压缩，JSON 还是比 XML 要小，而且耗时更少。 3 正如 Sumaray 和 Makki 在实验中指出的那样，JSON 在很多方面都比 XML 更具优势，得出同样结果的还有 Nurseitov、Paulson、Reynolds 和 Izurieta。首先，由于 JSON 文件天生的简洁性，与包含相同信息的 XML 相比，JSON 总是更小，这意味着更快的传输和处理速度。第二，在不考虑大小的情况下，两组研究 4 5 表明使用 JSON 执行序列化和反序列化的速度显著优于使用 XML。第三，后续的研究指出 JSON 的处理在 CPU 资源的使用上也优于 XML。研究人员发现 JSON 在总体上使用的资源更少，其中更多的 CPU 资源消耗在用户空间，系统空间消耗的 CPU 资源较少。这一实验是在 RedHat 的设备上进行的，RedHat 表示更倾向于在用户空间使用 CPU 资源。 6 不出意外，Sumaray 和 Makki 在研究里还说明了在移动设备上 JSON 的性能也优于 XML。 7 这是有道理的，因为 JSON 消耗的资源更少，而移动设备的性能也更弱。JSON 的另一个优点在于其对对象和数组的表述和 宿主语言 host language 中的数据结构相对应，例如 对象 object 、 记录 record 、 结构体 struct 、 字典 dictionary 、 哈希表 hash table 、 键值列表 keyed list 还有 数组 array 、 向量 vector 、 列表 list ，以及对象组成的数组等等。 8 虽然 XML 里也能表达这些数据结构，也只需调用一个函数就能完成解析，而往往需要更多的代码才能正确的完成 XML 的序列化和反序列化处理。而且 XML 对于人类来说不如 JSON 那么直观，XML 标准缺乏对象、数组的标签的明确定义。当结构化的标记可以替代嵌套的标签时，JSON 的优势极为突出。JSON 中的花括号和中括号则明确表示了数据的结构，当然这一优势也包含前文中的问题，在表示元数据时 JSON 不如 XML 准确。虽然 XML 支持 命名空间 namespace 与 前缀 prefix ，但这不代表 JSON 没有处理命名冲突的能力。比起 XML 的前缀，它处理命名冲突的方式更简洁，在程序中的处理也更自然。在 JSON 里，每一个对象都在它自己的命名空间中，因此不同对象内的元素名称可以随意重复。在大多数编程语言中，不同的对象中的成员可以包含相同的名字，所以 JSON 根据对象进行名称区分的规则在处理时更加自然。也许 JSON 比 XML 更优的部分是因为 JSON 是 JavaScript 的子集，所以在 JavaScript 代码中对它的解析或封装都非常的自然。虽然这看起来对 JavaScript 程序非常有用，而其他程序则不能直接从中获益，可实际上这一问题已经被很好的解决了。现在 JSON 的网站的列表上展示了 64 种不同语言的 175 个工具，它们都实现了处理 JSON 所需的功能。虽然我不能评价大多数工具的质量，但它们的存在明确了开发者社区拥抱 JSON 这一现象，而且它们切实简化了在不同平台使用 JSON 的难度。二者的动机简单地说，XML 的目标是标记文档。这和 JSON 的目标想去甚远，所以只要用得到 XML 的地方就尽管用。它使用树形的结构和包含语义的文本来表达混合内容以实现这一目标。在 XML 中可以表示数据的结构，但这并不是它的长处。JSON 的目标是用于数据交换的一种结构化表示。它直接使用对象、数组、数字、字符串、布尔值这些元素来达成这一目标。这完全不同于文档标记语言。正如上面说的那样，JSON 没有原生支持 混合内容 mixed content 的记录。软件这些主流的开放 API 仅提供 XML： 亚马逊产品广告 API Amazon Product Advertising API 。这些主流 API 仅提供 JSON： 脸书图 API Facebook Graph API 、 谷歌地图 API Google Maps API 、 推特 API Twitter API 、AccuWeather API、Pinterest API、Reddit API、Foursquare API。这些主流 API 同时提供 XML 和 JSON： 谷歌云存储 Google Cloud Storage 、 领英 API Linkedin API 、Flickr API。根据 可编程网络 Programmable Web 9 的数据，最流行的 10 个 API 中只有一个是仅提供 XML 且不支持 JSON 的。其他的要么同时支持 XML 和 JSON，要么只支持 JSON。这表明了大多数应用开发者都更倾向于使用支持 JSON 的 API，原因大概是 JSON 更快的处理速度与良好口碑，加之与 XML 相比更加轻量。此外，大多数 API 只是传递数据而非文档，所以 JSON 更加合适。例如 Facebook 的重点在于用户的交流与帖子，谷歌地图则主要处理坐标和地图信息，AccuWeather 就只传递天气数据。总之，虽然不能说天气 API 在使用时究竟是 JSON 用的多还是 XML 用的多，但是趋势明确偏向了 JSON。 10 11 这些主流的桌面软件仍然只是用 XML：Microsoft Word、Apache OpenOffice、LibraOffice。因为这些软件需要考虑引用、格式、存储等等，所以比起 JSON，XML 优势更大。另外，这三款程序都支持混合内容，而 JSON 在这一点上做得并不如 XML 好。举例说明，当用户使用 Microsoft Word 编辑一篇论文时，用户需要使用不同的文字字形、文字大小、文字颜色、页边距、段落格式等，而 XML 结构化的组织形式与标签属性生来就是为了表达这些信息的。这些主流的数据库支持 XML：IBM DB2、Microsoft SQL Server、Oracle Database、PostgresSQL、BaseX、eXistDB、MarkLogic、MySQL。这些是支持 JSON 的主流数据库：MongoDB、CouchDB、eXistDB、Elastisearch、BaseX、MarkLogic、OrientDB、Oracle Database、PostgreSQL、Riak。在很长一段时间里，SQL 和关系型数据库统治着整个数据库市场。像 甲骨文 Oracle 和 微软 Microsoft 这样的软件巨头都提供这类数据库，然而近几年 NoSQL 数据库正逐步受到开发者的青睐。也许是正巧碰上了 JSON 的普及，大多数 NoSQL 数据库都支持 JSON，像 MongoDB、CouchDB 和 Riak 这样的数据库甚至使用 JSON 来存储数据。这些数据库有两个重要的特性是它们适用于现代网站：一是它们与关系型数据库相比 更容易扩展 more scalable ；二是它们设计的目标就是 web 运行所需的核心组件。 12 由于 JSON 更加轻量，又是 JavaScript 的子集，所以很适合 NoSQL 数据库，并且让这两个品质更容易实现。此外，许多旧的关系型数据库增加了 JSON 支持，例如 Oracle Database 和 PostgreSQL。由于 XML 与 JSON 间的转换比较麻烦，所以大多数开发者会直接在他们的应用里使用 JSON，因此开发数据库的公司才有支持 JSON 的理由。（LCTT 译注：NoSQL 是对不同于传统的关系数据库的数据库管理系统的统称。参考来源） 13 未来对互联网的种种变革中，最让人期待的便是 物联网 Internet of Things （IoT）。这会给互联网带来大量计算机之外的设备，例如手表、温度计、电视、冰箱等等。这一势头的发展良好，预期在不久的将来迎来爆发式的增长。据估计，到 2020 年时会有 260 亿 到 2000 亿的物联网设备被接入互联网。 14 15 几乎所有的物联网设备都是小型设备，因此性能比笔记本或台式电脑要弱很多，而且大多数都是嵌入式系统。因此，当它们需要与互联网上的系统交换数据时，更轻量、更快速的 JSON 自然比 XML 更受青睐。 16 受益于 JSON 在 web 上的快速普及，与 XML 相比，这些新的物联网设备更有可能从使用 JSON 中受益。这是一个典型的梅特卡夫定律的例子，无论是 XML 还是 JSON，抑或是什么其他全新的格式，现存的设备和新的设备都会从支持最广泛使用的格式中受益。Node.js 是一款服务器端的 JavaScript 框架，随着她的诞生与快速成长，与 MongoDB 等 NoSQL 数据库一起，让全栈使用 JavaScript 开发成为可能。这些都预示着 JSON 光明的未来，这些软件的出现让 JSON 运用在全栈开发的每一个环节成为可能，这将使应用更加轻量，响应更快。这也是任何应用的追求之一，所以，全栈使用 JavaScript 的趋势在不久的未来都不会消退。 17 此外，另一个应用开发的趋势是从 SOAP 转向 REST。 18 19 20 XML 和 JSON 都可以用于 REST，可 SOAP 只能使用 XML。从这些趋势中可以推断，JSON 的发展将统一 Web 的信息交换格式，XML 的使用率将继续降低。虽然不应该把 JSON 吹过头了，因为 XML 在 Web 中的使用依旧很广，而且它还是 SOAP 的唯一选择，可考虑到 SOAP 到 REST 的迁移，NoSQL 数据库和全栈 JavaScript 的兴起，JSON 卓越的性能，我相信 JSON 很快就会在 Web 开发中超过 XML。至于其他领域，XML 比 JSON 更好的情况并不多。角注Introducing JSON ↩XML Tutorial ↩JSON vs. XML: Some hard numbers about verbosity ↩Comparison of JSON and XML Data Interchange Formats: A Case Study ↩A comparison of data serialization formats for optimal efficiency on a mobile platform ↩Comparison of JSON and XML Data Interchange Formats: A Case Study ↩A comparison of data serialization formats for optimal efficiency on a mobile platform ↩Introducing JSON ↩Most Popular APIs: At Least One Will Surprise You ↩Why JSON will continue to push XML out of the picture ↩Thousands of APIs Paint a Bright Future for the Web ↩Why JSON will continue to push XML out of the picture ↩How JSON sparked NoSQL – and will return to the RDBMS fold ↩A Simple Explanation Of ‘The Internet Of Things’ ↩Proofpoint Uncovers Internet of Things (IoT) Cyberattack ↩Why JSON will continue to push XML out of the picture ↩Why JSON will continue to push XML out of the picture ↩Thousands of APIs Paint a Bright Future for the Web ↩3,000 Web APIs: Trends From A Quickly Growing Directory ↩How REST replaced SOAP on the Web: What it means to you ↩via: https://www.cs.tufts.edu/comp/150IDS/final_papers/tstras01.1/FinalReport/FinalReport.html作者：TOM STRASSNER 选题：lujun9972 译者：wwhio 校对：wxy本文由 LCTT 原创编译，Linux中国 荣誉推出
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 消息如下所示： 消息中只有一小部分类似于今天我们所知的 JSON，本身其实是一个包含有一些 JavaScript 的 HTML 文档。类似于 JSON 的部分只是传递给名为 receive() 的函数的 JavaScript 对象字面量。Crockford 和 Morningstar 决定滥用 HTML 的帧（ ）以发送数据。他们可以让一个帧指向一个返回的上述 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 的冗长啰嗦及“尖括号负担”。 11 XML 中每个开始标记都必须与结束标记匹配，这意味着 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，以确保得到更新的通知。http://www.cs.tufts.edu/comp/150IDS/final_papers/tstras01.1/FinalReport/FinalReport.html#software ↩https://insights.stackoverflow.com/trends?tags=json%2Cxml%2Cprotocol-buffers%2Cyaml%2Ccsv ↩Zakas, Nicholas C., et al. “What Is Ajax?” Professional Ajax, 2nd ed., Wiley, 2007. ↩https://youtu.be/-C-JoyNuQJs?t=32s ↩http://adaptivepath.org/ideas/ajax-new-approach-web-applications/ ↩同上 ↩http://scripting.com/2006/12/20.html ↩http://blogoscoped.com/archive/2009-03-05-n15.html ↩https://scripting.wordpress.com/2006/12/20/scripting-news-for-12202006/#comment-26383 ↩http://www.json.org/xml.html ↩https://blog.codinghorror.com/xml-the-angle-bracket-tax ↩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中国 荣誉推出
MySQL 文档存储 可以跳过底层数据结构创建、数据规范化和其它使用传统数据库时需要做的工作，直接存储数据。MySQL 可以提供 NoSQL JSON 文档存储 Document Store 了，这样开发者保存数据前无需 规范化 normalize 数据、创建数据库，也无需在开发之前就制定好数据样式。从 MySQL 5.7 版本和 MySQL 8.0 版本开始，开发者可以在表的一列中存储 JSON 文档。由于引入 X DevAPI，你可以从你的代码中移除令人不爽的结构化查询字符串，改为使用支持现代编程设计的 API 调用。系统学习过结构化查询语言（SQL）、 关系理论 relational theory 、 集合 set 和其它关系数据库底层理论的开发者并不多，但他们需要一个安全可靠的数据存储。如果数据库管理人员不足，事情很快就会变得一团糟，MySQL 文档存储 允许开发者跳过底层数据结构创建、数据规范化和其它使用传统数据库时需要做的工作，直接存储数据。只需创建一个 JSON 文档集合 document collection ，接着就可以使用了。JSON 数据类型所有这一切都基于多年前 MySQL 5.7 引入的 JSON 数据类型。它允许在表的一行中提供大约 1GB 大小的列。数据必须是有效的 JSON，否则服务器会报错；但开发者可以自由使用这些空间。X DevAPI旧的 MySQL 协议已经历经差不多四分之一个世纪，已经显现出疲态，因此新的协议被开发出来，协议名为 X DevAPI。协议引入高级会话概念，允许代码从单台服务器扩展到多台，使用符合 通用主机编程语言样式 common host-language programming patterns 的非阻塞异步 I/O。需要关注的是如何遵循现代实践和编码风格，同时使用 CRUD （Create、 Read、 Update、 Delete）样式。换句话说，你不再需要在你精美、纯洁的代码中嵌入丑陋的 SQL 语句字符串。一个新的 shell 支持这种新协议，即所谓的 MySQL Shell。该 shell 可用于设置 高可用集群 high-availability cluster 、检查服务器 升级就绪状态 upgrade readiness 以及与 MySQL 服务器交互。支持的交互方式有以下三种：JavaScript，Python 和 SQL。代码示例下面的代码示例基于 JavaScript 方式使用 MySQL Shell，可以从 JS> 提示符看出。下面，我们将使用用户 dstokes 、密码 password 登录本地系统上的 demo 库。db 是一个指针，指向 demo 库。$ mysqlsh dstokes:password@localhost/demo JS> db.createCollection("example") JS> db.example.add( { Name: "Dave", State: "Texas", foo : "bar" } ) JS> 在上面的示例中，我们登录服务器，连接到 demo 库，创建了一个名为 example 的集合，最后插入一条记录；整个过程无需创建表，也无需使用 SQL。只要你能想象的到，你可以使用甚至滥用这些数据。这不是一种代码对象与关系语句之间的映射器，因为并没有将代码映射为 SQL；新协议直接与服务器层打交道。Node.js 支持新 shell 看起来挺不错，你可以用其完成很多工作；但你可能更希望使用你选用的编程语言。下面的例子使用 world_x 示例数据库，搜索 _id 字段匹配 CAN. 的记录。我们指定数据库中的特定集合，使用特定参数调用 find 命令。同样地，操作也不涉及 SQL。var mysqlx = require('@mysql/xdevapi'); mysqlx.getSession({ //Auth to server host: 'localhost', port: '33060', dbUser: 'root', dbPassword: 'password' }).then(function (session) { // use world_x.country.info var schema = session.getSchema('world_x'); var collection = schema.getCollection('countryinfo'); collection // Get row for 'CAN' .find("$._id == 'CAN'") .limit(1) .execute(doc => console.log(doc)) .then(() => console.log("\n\nAll done")); session.close(); }) 下面例子使用 PHP，搜索 _id 字段匹配 USA 的记录：<?PHP // Connection parameters $user = 'root'; $passwd = 'S3cret#'; $host = 'localhost'; $port = '33060'; $connection_uri = 'mysqlx://'.$user.':'.$passwd.'@'.$host.':'.$port; echo $connection_uri . "\n"; // Connect as a Node Session $nodeSession = mysql_xdevapi\getNodeSession($connection_uri); // "USE world_x" schema $schema = $nodeSession->getSchema("world_x"); // Specify collection to use $collection = $schema->getCollection("countryinfo"); // SELECT * FROM world_x WHERE _id = "USA" $result = $collection->find('_id = "USA"')->execute(); // Fetch/Display data $data = $result->fetchAll(); var_dump($data); ?> 可以看出，在上面两个使用不同编程语言的例子中，find 操作符的用法基本一致。这种一致性对跨语言编程的开发者有很大帮助，对试图降低新语言学习成本的开发者也不无裨益。支持的语言还包括 C、Java、Python 和 JavaScript 等，未来还会有更多支持的语言。从两种方式受益我会告诉你使用 NoSQL 方式录入的数据也可以用 SQL 方式使用？换句话说，我会告诉你新引入的 NoSQL 方式可以访问旧式关系型表中的数据？现在使用 MySQL 服务器有多种方式，作为 SQL 服务器，作为 NoSQL 服务器或者同时作为两者。via: https://opensource.com/article/18/6/mysql-document-store作者：Dave Stokes 选题：lujun9972 译者：pinewall 校对：wxy本文由 LCTT 原创编译，Linux中国 荣誉推出