格式校验利器:JSON Schema 简介

JSON 是什么

JSON,全称 JavaScript Object Notation. 是 JS 的数据结构的子集。相信大家对 JSON 也非常熟悉了。JSON 一共只有六种数据结构,这里列举他们在 JS 和 Python 中的叫法以及在 JSON 中的例子:

JSPython3JSON
stringstring"ABC"
numberint/float123 -1.23
booleanbooltrue false
nullNonenull
objectdictnu{"key": "value"}
arraylist["ABC", 123, true]

由于 JSON 是 JS 语法的子集,所以下文中我都会使用 JS 的称呼。

得益于 JSON 极致的简单和 JS 的广泛使用,JSON 很快流行了起来。但是简单也有简单的坏处,人们逐渐发现 JSON 缺少一些必要的功。

JSON Schema 是什么?

当前后端使用 JSON 通信的时候,双方需要验证 JSON 的数据格式,比如验证 array 的长度、number 的大小、甚至于类似 object 中有某两个属性不能并存等等要求。当然这些规则都可以使用代码进行验证,但是这样写起来太繁琐了,所以为了描述并校验 JSON 的格式,JSON Schema 诞生了。

这里有一个非常简单的 JSON Schema 的例子:

{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "type": "object",
  "properties": {
    "name": { "type": "string" },
    "email": { "type": "string" },
    "age": {
      "type": "integer",
      "minimum": 0,
      "exclusiveMinimum": false
    },
    "telephone": {
      "type": "string",
      "pattern": "^(\\([0-9]{3}\\))?[0-9]{3}-[0-9]{4}$"
    }
  },
  "required": ["name", "email"],
  "additionalProperties": false
}

上面这个 JSON 描述了这样的一个 JSON:

  • 类型是 object
  • 有四个属性:
    • name:
      • 类型为 string
    • email:
      • 类型为 string
    • Age:
      • 类型为整数
      • 这个整数要大于等于 0
    • telephone:
      • 类型为 string
      • 需要符合这个正则表达式
  • 上面这四个属性中必须出现有的 name 和 email,剩下的两个是可选的
  • 除了这四个属性外,不允许出现其他属性

比如说,这样的一个 JSON 就符合这些要求:

{
  "name": "Sherlock Holmes",
  "email": "sherlock@gmail.com",
  "age": 164
}

可以看到,JSON Schema 本身也是一个 JSON。同一个 JSON,同一个世界前后端都能看懂,那么的话这个 JSON Schema 就既可以作为执行代码,又可以作为文档。这种统一确保了代码和文档不会脱节,省去了写文档的功夫。毕竟程序员最讨厌的就是没有文档的代码和写文档了。

Understanding JSON Schema

在这里,我强烈推荐阅读 Understanding JSON Schema,以至于我要专门写一章来介绍它。这是一个非常适合初学者的教程,有着及其清晰的例子。虽然截止写稿时其版本还停留在 draft-04,不过瑕不掩瑜,依然是我心中最优秀的 JSON Schema 教程。

在这里列一下 Understanding JSON Schema 的大纲,方便查漏补缺:

  • Type-specific keywords
    • string
      • minLength
      • maxLength
      • pattern
    • Numeric types
      • types: integer, number
      • multipleOf
      • range: minimum, exclusiveMinimum, maximum, exclusiveMaximum
    • object
      • properties
      • additionalProperties
      • required
      • size: minProperties, maxProperties
      • dependencies
      • patternProperties
    • array
      • items
      • length: minItems, maxItems
      • uniqueItems
    • boolean
    • null
  • Generic keywords
    • Metadata: title, description, default
    • Enumerated values: enum
  • Combining schemas
    • allOf
    • anyOf
    • oneOf
    • not
  • The $schema keyword
  • Regular Expressions

版本

截止到今天,JSON Schema 一共有 7 个版本,最新的是 draft-07。不同版本之间并不(完全)兼容,所以最佳实践是在写 JSON Schema 的时候使用 $schema 关键字标记当前使用的是哪个规范。

下面举个例子,展示了 draft-04 和 draft-06 中「大于」和「大于等于」写法:

大于:

{
  "$schema": "https://json-schema.org/draft-06/schema#",
  "type": "number",
  "exclusiveMinimum": 0
}
{
  "$schema": "https://json-schema.org/draft-04/schema#",
  "type": "number",
  "minimum": 0,
  "exclusiveMinimum": true
}

大于等于:

{
  "$schema": "https://json-schema.org/draft-06/schema#",
  "type": "number",
  "minimum": 0
}
{
  "$schema": "https://json-schema.org/draft-04/schema#",
  "type": "number",
  "minimum": 0,
  "exclusiveMinimum": false
}

注:在 draft-04 中,exclusiveMinimum 的默认值就是 false,所以可以不写。

在 draft-04 中,exclusiveMinimum 的值是 boolean,draft-06 中改成了 number。在相同含义的情况下,新版的写法的确更加简洁。

关于不同版本之间的区别可以查看官网的 Migrating from older drafts 一节。

总结

JSON Schema 作为一个验证 JSON 的通用语法,我认为它成功完成了自己的任务。虽然有一些诸如可读性不高,写法繁琐的缺点,但是我认为这些都是必要的妥协。

欢迎大家去掘金评论本文。