作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.
Seva Safris的头像

Seva Safris

Seva是一位拥有20年行业经验的企业和创业老手,毕业于加州大学伯克利分校的EECS和MSE专业.

专业知识

以前在

电子艺界
Share

In Part 1 在本文中,我们更深入地研究了Web 1的发展.0 to 2.0, and how its growing pains gave rise to XML and JSON. 随着JavaScript在过去十年中对软件趋势的影响, JSON比其他任何数据交换格式都受到越来越多的关注. 博客圈发表了无数篇比较这两种标准的文章,并逐渐扩大了对JSON简单性的赞扬和对XML冗长性的批评. JSON就比XML好吗? 对数据交换的深入研究将揭示JSON和XML的优缺点, 以及每个标准对通用应用程序与企业的适用性.

在本文的第2部分中,我们将:

  1. 评估JSON和XML之间的基本区别, 并将每个标准对简单应用和复杂应用的适用性联系起来.
  2. 探讨JSON和XML如何影响普通应用程序和企业的软件风险, 并探索每个标准可能的缓解方法.

The software landscape is broad and wide, differentiated by a variety of platforms, 语言, 业务上下文, 和规模. The term “scale” is often associated to user interaction, 但是也用来指区分简单和复杂的软件质量:“开发规模”,“投资规模。,和“复杂程度”.尽管所有的开发人员——无论是个人还是团队——都渴望降低他们的应用程序的软件风险, 严格遵守需求和规范将普通应用程序与企业应用程序区分开来. 关于JSON和XML, 对这种严谨性的考虑揭示了优点和缺点, and fundamental differences of the two standards.

JSON for Common Applications Versus the 企业

为了评估JSON和XML对通用应用程序和企业的适用性, 让我们定义数据交换, 并提供一个简单的用例来帮助阐明两个标准之间的差异.

数据交换涉及两个端点,它们被区分为两个参与者:生产者和消费者. The simplest form of data interchange is one-directional, 它涉及三个一般阶段:数据由生产者产生(1), 数据交换(2), and data is consumed by the consumer (3).

XML vs JSON: Data interchange between two endpoints

单向数据交换用例只有在生产者和消费者是相同的逻辑参与者时才能进一步简化, 例如,当数据交换用于磁盘存储时,应用程序在时间$\xi_1$产生数据, 在时间$\xi_2$处消耗它.

XML与JSON:应用程序与其存储之间的数据交换

不考虑这种简化, 用例的重点是确定数据交换正在解决的问题. 位于数据生产(1)和数据消费(3)之间, 数据交换(2)为生产者和消费者之间的数据交换提供了一种格式. 没有比这更简单的了!

JSON作为数据交换

Crockford自己声称JSON的一个巨大优势是JSON被设计为一种数据交换格式.1 从一开始,它的目的就是在程序之间以简洁的方式传递结构化的信息. Crockford defined JSON in a succinct 9 page RFC document, 用简洁的语义描述一种格式,以明确其设计目的:数据交换.2

JSON满足了在两个参与者之间交换数据的直接需求, 高效和有效, but in the real world of software development, 即时需求通常只是其他需求三明治中的第一层. 对于简单应用程序, developed to satisfy a simple specification, a simple data interchange is all that is necessary. 对于大多数常见的应用程序, 数据交换需求的简单性表明JSON是最佳解决方案. However, 随着围绕数据交换的需求变得复杂, the deficiencies of JSON are revealed.

围绕数据交换的复杂需求涉及定义生产者和消费者之间的业务关系的更高考虑. 例如, 软件系统的演化路径在其架构和参考实现时可能不为人所知. 将来可能需要添加或删除消息格式中的对象或属性. 如果生产者或消费者涉及大量当事人, 系统可能还需要支持消息格式的当前和以前的版本. Referred to as consumer-driven contracts (CDC), 服务提供者的一个常见问题是发展生产者和消费者之间的契约.3

消费者驱动的契约

让我们扩展我们的用户用例,以包括欧洲银行和零售商之间的消费者驱动的合同,该合同为购买传达客户的银行信息.

SWIFT是最早的银行交易电子通信标准之一.4 Stripped of its purchase information, 裸SWIFT消息包括识别账户的SWIFT代码.

{
  “代码”:“CTBAAU2S”
}

此消息带有属性 code, has a certain value that represents its 状态空间. The 状态空间 消息被定义为所有可以表达的状态的完整空间. 通过区分有效和无效的状态,我们可以隔离的错误部分 状态空间,并将其称为 错误的空间. 设$\xi$表示 错误的空间 其中$\xi = 1$表示所有有效和无效状态的空间(i.e.,所有的状态 状态空间), and $\xi = 0$ the space of only valid states (i.e.的有效状态 状态空间).

代码的不同状态

数据交换层是使用者的入口点,它的 错误的空间 $\xi_0$(下标0用于表示入口点层)由所有可能的输入(有效和无效)与有效输入的比例决定. 输入消息的有效性由两层定义:技术层和逻辑层. For JSON, 技术正确性取决于消息与Crockford的RFC文档中指定的JSON语义的语法遵从性.2逻辑正确性, however, 涉及确定帐户标识符的有效性, SWIFT标准定义:“长度为8或11个字母数字字符,结构符合ISO 9362。.”5

After years of successful operation, 欧洲银行和零售商被要求修改其消费者驱动的合同,以支持新的账户识别标准:IBAN. 修改JSON消息以支持新的属性表示 type,将标识符区分为 SWIFT or IBAN.

{
  “类型”:“伊班人”,
  "code": "DE91 1000 0000 0123 4567 89"
}

之后, 这家欧洲银行决定进入美国市场,并与美国的零售商签订了以消费者为导向的合同. 银行现在必须支持ACH账户识别系统,扩大对消息的支持 类型:“ACH”.

{
  “类型”:“哦”,
  “代码”:“379272957729384”,
  “路由”:“021000021”
}

对JSON消息格式的每个扩展都会增加应用程序的系统风险. 在该银行整合SWIFT时,不知道IBAN和ACH是否会得到支持. 大量的零售商与银行交换数据, 出错的风险就更大了, 促使银行设计一个解决方案来降低这种风险.

欧洲银行 is an enterprise solution, 并坚决主张其严格保证与零售商的无差错操作. 为了减轻软件错误的风险,银行努力减少 错误的空间 $\xi_0$取值范围为1到0. To help banking systems reduce software risk, 管理SWIFT的组织, IBAN, 和ACH代码标准定义了简单的测试函数来确定标识符的逻辑正确性. 每个标准的测试函数可以用正则表达式形式表示:

𝒗SWIFT (id) = regexid(“[a - z] {6} [A-Z0-9] {2} [A-Z0-9] {3})?” )
𝒗IBAN   (id) = regexid( “[A-Z]{2}\d{2} ?\d{4} ?\d{4} ?\d{4} ?\d{4} ?\d{0,2}” )
𝒗ACH (id, rt) = regexid (\ w{1, 17})×regexrt (\ d{9}”)

使$\xi_0$更接近于0, 银行的系统必须对每条输入信息进行足够仔细的测试,以确保它能检测到尽可能多的可能遇到的错误. Since JSON is only capable of data interchange, 验证逻辑必须在系统本身的一个层中实现. 这种逻辑的一个例子是:

isValid(消息){
  if (!消息| | !message.type) {
    返回错误;
  }
  如果(消息.type == "SWIFT") {
    返回消息.code &&
           message.code.matches("[A-Z]{6}[A-Z0-9]{2}([A-Z0-9]{3})?"));
  }
  如果(消息.type == "IBAN") {
    返回消息.code &&
           message.code.匹配(“[a - z] {2} \ d {2} ?\d{4} ?\d{4} ?\d{4} ?\d{4} ?\d{0,2}"));
  }
  如果(消息.type == "ACH") {
    返回消息.code &&
           message.code.匹配(“\ w{1, 17}”) &&
           message.routing &&
           message.routing.匹配(" \ d {9} "));
  }
  返回错误;
}

验证逻辑的规模与被验证的消息变体的复杂性成正比. 这家欧洲银行精简后的信息格式很简单, 因此,其验证逻辑的规模相应地简单. 对于具有更多属性的消息, and especially those with nested objects, the validation footprint can grow tremendously, in both line-count and logical complexity. 使用JSON作为数据交换, 验证层作为系统的耦合组件实现. Since the validation layer consumes input messages, it incurs the $\xi_0$ of the data interchange layer.

Every layer in a software system has a value $\xi$. 数据交换之上的验证层由于其自身的复杂性,有自己的潜在错误状态空间. For the European Bank, this complexity is the isValid(消息) function. 因为验证层使用来自数据交换层的输入, its $\xi_1$ becomes a function of $\xi_0$. 这种关系的抽象外推表明,一层的$\xi$吸收前一层的$\xi$, which absorbs $\xi$ of the previous layer, 等等......。. 这种关系可以用下面的表达式表示:

$\xi_n = \alpha_n \times \xi_{n - 1}$

这里,$\xi_n$表示 错误的空间 of layer $n$ as a function of $\xi_{n-1}$ (i.e. the 错误的空间 (前一层), 乘以$\alpha_n$, 哪个表示由层$n$本身的软件复杂性(i.e.在第n层中,错误的复合而不是代码的复杂性。.

一个有弹性的系统是通过减少其输入的复杂性空间来降低其软件风险的系统, 它的内部处理逻辑, 以及它的输出.

The Software Risk of Complex Requirements

我们所说的“系统的软件风险”相当于$\xi_N$, where $N$ represents the highest layer in the system. 在构建新应用程序时, 由于实现的可见性有限,计算$\xi_N$可能具有挑战性. However, 可以通过分析需求的复杂性来估计整个系统的复杂性.

软件风险与需求的复杂性成正比. 随着需求复杂性的增加,出现bug的空间也在增加. 消息格式越复杂,系统必须考虑的错误情况就越多. 特别是, for service providers involving disparate systems, 服务提供者(消费者)必须对来自其客户(生产者)的消息采取防御策略。. 服务提供者不能假定从其客户端交换的数据没有错误. 为了避免错误的输入数据,使用者验证每条消息的内容.

For applications with complex requirements, and enterprise systems 特别是, the mitigation of software risk is a prime consideration.

To mitigate the risk of logical complexity, “封装”的编程原则帮助开发人员将代码组织成具有逻辑边界的层. 在每个封装层,输入被验证、处理并交付给下一层. 这一原则为逐步减少每一层的“误差空间”提供了一种有条理的方法, 这进一步将高级业务逻辑与由于错误输入而导致的错误隔离开来. Proper encapsulation results in higher cohesion, 因此,特定封装模块的职责被明确定义,并且不与辅助职责耦合. 有一个清晰的思想分离, 降低了消息可变性的“特定风险”, 以及上面每层数据交换的“一般风险”$\xi_n$, 以及整个应用程序的“总风险”$\xi_N$.

JSON作为数据交换 for Complex Requirements

With JSON as the data interchange format, 通信协议的复杂需求必须在JSON之外的层中处理. Though JSON is great due to its simple semantics, 围绕数据交换的复杂性最终不可避免地波及到应用程序的其他层. To hedge the risk of errors due to input complexity, 系统依靠封装将消息验证与业务逻辑分离. JSON’s close integration with JavaScript, however, 引导开发人员将此逻辑集成到应用程序堆栈中更高的层中, often intertwined with the 业务逻辑 itself. 不幸的是, 这种方法受到json过于简单的特性的鼓励——封装消息格式的不同变体的转换逻辑的中间层是降低风险的解决方案, but such patterns are often seen as excessive, 不受欢迎的, 因此很少使用. 检查账户标识符是否有效的逻辑可以实现在:

  1. A validation layer just above data interchange, 导致责任和逻辑内聚的封装.
  2. 在业务逻辑中,导致职责合并和逻辑耦合.

JSON作为数据交换需要将验证逻辑吸收到应用程序代码中. 实现 isValid(消息) 应用程序中的功能导致消息格式的逻辑含义(由消费者驱动的契约协议的生产者和消费者双方在协商中决定的格式)与消息处理的功能实现之间的耦合. 消费者驱动的契约, 数据交换的范围扩展到包括验证和支持消息变体的处理代码. 从这个角度来看, JSON is the small center of an onion, 洋葱的层表示验证所需的附加逻辑, 处理, 业务逻辑, 等等......。.

The many layers of an application and where JSON fits in

a. JSON作为数据交换
b. 封装消息验证. This code is coupled to the application.
c. 未封装消息验证. This code is intermixed with the business layers.
d. 应用程序.

在为应用程序选择最佳数据交换格式时, is it only the center of the onion we care about, 还是整个洋葱?

在消费者中实现的自定义验证逻辑不能被生产者使用,反之亦然. 特别是如果系统是在不同的平台上实现的, 期望在通信协议的各方之间共享验证功能是不合理的. Only if two systems share the same platform, 验证函数被正确封装并与应用程序解耦, 代码可以共享吗. 然而,这种完美条件的独特用例是不现实的. As validation logic gets more complex, 它在应用程序中的直接集成可能导致意外的(或预期的)耦合, thus rendering the function unshareable. 欧洲银行, 例如, 是否希望通过要求零售商验证其终端上的所有消息来进一步降低其软件风险. 为了实现这一目标,银行和零售商将被要求实施他们自己的版本 isValid(消息). 由于每一方都将负责自己的实现,这将:

  1. 为每一方的执行留下差异的空间.
  2. 对于每一个政党的制度, 将消息格式的逻辑含义与技术实现相结合以进行处理.

两个应用程序之间可视化的JSON数据交换,每个应用程序都表示为分层的“洋葱”。

虚线表示缺乏用于表达消费者驱动契约的规范标准,从而在功能上约束生产者和消费者. 为了执行消费者驱动的契约,每一方都要对自己的实现负责.

Open-source Libraries to Bridge the Gap

JSON作为简单高效的数据交换标准脱颖而出, but it does not provide standard patterns to help 开发人员 用洋葱的外层. To address the intricacies of data interchange, 开发人员可以选择两种通用的架构方法:

  1. 实现自定义代码,例如 isValid(消息) above, at the cost of functional and logical coupling.
  2. 集成实现与问题匹配的通用解决方案的第三方库, 以产生第三方库的软件风险为代价.

已经开发了许多开源解决方案来解决围绕数据交换的常见复杂问题. 在一般情况下, 这些解决方案是具体的, designed to address one part of the problem, 但不是另一个. OpenAPI, 例如, 是一个为REST api定义接口描述格式的开源规范.6 OpenAPI解决了REST服务的发现和规范问题, 但消费者驱动型合同的规范执行不在其范围之内. GraphQL是另一个流行的库,它是由Facebook开发的,用于实现对现有数据的查询.7 GraphQL通过将数据层中的api直接暴露给生产者的数据交换,为消费者提供了一种标准化的方式来访问生产者的数据. 对于简单应用程序 with simple data, 将数据层直接连接到传输层可以为开发人员节省大量的样板代码. 对于企业系统, however, 坚持降低应用程序中的软件风险, GraphQL方法减少了封装, 内聚性降低, 以及耦合的增加.

With JSON as the data interchange format, 公司和个人开发人员只能靠自己来拼凑一组库和框架,以满足复杂的需求. 每个解决方案旨在解决问题的一部分, 但不是另一个, 对于开发者来说,找到以下解决方案变得越来越困难:

  1. 解决数据交换的复杂性,以满足系统的需求和规范.
  2. 保持高封装和高内聚,低耦合,以降低软件的总风险.

Due to the non-standard nature of these solutions, 项目吸收了拼图中每个组成部分的软件风险.

依赖较多外部库的应用程序比依赖较少外部库的应用程序承担更高的软件风险. 以减轻复杂应用程序的软件风险, 开发人员和软件架构师经常选择依赖标准而不是定制解决方案. 应用程序不需要担心标准会改变, break, 甚至不存在. With custom solutions, however, this is not the case. 自定义解决方案,集成了开源项目的混合包, 例如, absorb the risk of each constituent dependency.

The Strengths and Weaknesses of JSON

Compared to other data interchange standards, JSON是最简单的, 最紧凑的人类可读格式. 对于简单应用程序, developed to satisfy a simple specification, a simple data interchange is all that is necessary. However, 随着围绕数据交换的需求变得越来越复杂, the deficiencies of JSON are revealed.

就像HTML的“分歧灾难”导致了XML的发明, 在依赖JSON进行数据交换的复杂代码库中也可以实现类似的效果. JSON规范没有封装围绕数据交换的直接功能,这可能导致应用程序更高层中的逻辑碎片化. 使用JSON作为数据交换, 应用程序的更高层可以自行实现JSON本身所缺少的功能. 通过在应用程序的表层包含围绕数据交换的复杂性, 捕获与数据相关的错误的能力也被推到了应用程序的表面. 这种体系结构的副作用可能导致将与数据相关的错误直接表达给应用程序的用户, which for the enterprise is unacceptable.

JSON的根本缺陷是它没有为文档的逻辑成分的规范性描述提供通用标准.

对于努力断言无错误操作的企业应用程序, 使用JSON作为数据交换会导致模式增加软件风险,并导致意想不到的障碍,阻碍实现高代码质量的目标, 稳定, 以及对未来未知的应变能力.

JSON为数据交换提供了简洁的语义和基本的简单性, 但是它有可能导致系统范围内封装的减少. The scope of consumer-driven contracts, 或者围绕数据交换的其他复杂需求,如协议版本控制和内容验证, 必须在JSON之外解决吗. 适当的封装, 复杂的需求可以以内聚和非耦合的方式实现, 但在一开始,这将是一个专门的架构焦点. 以避免为适当的封装所需要的过多的努力, 开发人员经常实现模糊层间界限的解决方案, 因此,围绕数据交换的复杂性会扩散到应用程序的其他层,并导致潜在错误的逐步增加.

开发人员还可以用一组库和框架来弥补缺失的缺口,以满足更高的需求. By relying on a mixed bag of external libraries, however, 应用程序承担每个组件依赖项的风险. 因为这些解决方案是特定的, 旨在解决问题的一部分,而不是另一部分, this further leads to a reduction in encapsulation, 内聚性降低, 以及耦合的增加.

JSON是一种方便的格式,非常适合简单的应用程序, 但是,将其用于围绕数据交换的复杂需求会带来更高的软件风险. JSON may seem appealing due to its base simplicity, 但是对于企业系统, 特别是, 它的使用可能会导致不希望的模式,从而妨碍系统的代码质量, 稳定, and the resilience to future unknowns.

In Part 3 本文简介, 我们将探讨XML作为数据交换,并评估其与JSON相比的优缺点. With a deeper understanding of XML’s capabilities, 我们将探讨数据交换的未来,以及将XML的强大功能引入JSON的当前项目.

参考文献。

1. Douglas Crockford: JSON传奇 (Yahoo!, 2009年7月)

2. RFC 4627 (2006年7月,克罗克福德)

3. 消费者驱动的契约: A Service Evolution Pattern (MartinFowler.网站,2006年6月)

4. 全球银行间金融电信协会 (维基百科)

5. ISO 9362 (维基百科)

6. OpenAPI -昂首阔步

7. GraphQL - A query language for your API

了解基本知识

  • 什么是XML,为什么它很重要?

    XML provides a way for the industry to specify, 具有严格的语义, custom markup 语言 for any application. Considered the holy grail of computing, XML仍然是世界上最广泛使用的表示和交换信息的格式.

  • 什么是XML及其优点?

    XML是一种指定自定义标记语言的语言,并定义了断言XML文档数据完整性的模式标准. The XML Schema is detached from the application, 允许消息格式的逻辑含义与其处理的功能实现解耦.

  • JSON为何如此流行?

    JSON’s semantics map directly to JavaScript. With JavaScript’s proliferation in the last decade, JSON比其他任何数据交换格式都受到越来越多的关注. Due to its ease-of-use and short learning curve, JSON已经成为各种水平的开发人员最容易使用的格式.

  • JSON在哪里使用??

    JSON是JavaScript应用程序中数据交换的原生格式,用于在任何平台上的程序之间以简洁的方式携带结构化信息.

  • What is software risks, and is it avoidable?

    软件风险代表了应用程序对错误的弹性和对未来未知的延展性的反比. 所有的软件风险都是耦合的结果,无论是功能的还是逻辑的. Each application is coupled to the context of its purpose, making software risk an unavoidable certainty.

  • How do you identify risks in software projects?

    软件风险与需求的复杂性成正比. 在构建新应用程序时, 由于实现的可见性有限,评估软件风险可能具有挑战性. However, 可以通过分析需求的复杂性来估计整个系统的复杂性.

聘请Toptal这方面的专家.
Hire Now
Seva Safris的头像
Seva Safris

位于 泰国曼谷

成员自 2014年7月17日

作者简介

Seva是一位拥有20年行业经验的企业和创业老手,毕业于加州大学伯克利分校的EECS和MSE专业.

Toptal作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.

专业知识

以前在

电子艺界

World-class articles, delivered weekly.

订阅意味着同意我们的 隐私政策

World-class articles, delivered weekly.

订阅意味着同意我们的 隐私政策

Toptal开发者

加入总冠军® 社区.