JSON Schema 是一个描述和验证 JSON 数据结构的强大工具。Schema 可以理解为模式或规则。
文档:https://json-schema.org/understanding-json-schema/


定义一个最简单的 JSON Schema:{} 它包含许多关键字/属性。如:

关键字 描述 事例
$schema 声明 JSON Schema 状态(版本/规范控制) { “$schema”: “http://json-schema.org/schema#“ }
$id 唯一标识 URI { “$id”: “http://yourdomain.com/schemas/myschema.json“ }
title 标题 “Production”
description 描述 “a produciton info description”
type 类型 “number” | [“number”,”string”]
properties 属性配置 { “test”: { “type”: “number” } }

下面解析 JSON Schema 中包含的关键字含义及用法。

类型关键字

字符串(string)

长度:

属性 描述
minLength 字符串最小长度,非负数
maxLength 字符串最大长度,非负数
pattern 正则表达式
format 格式

内置 format 列表:

  • 日期和时间
    • “date-time”:日期和时间,如 2018-11-13T20:20:39+00:00。
    • “time”:时间,如 20:20:39+00:00。
    • “date”:日期,如 2018-11-13。
  • Email 地址
    • “email”:互联网电子邮件地址,请参阅 RFC 5322,第 3.4.1 节
    • “idn-email”:电子邮件地址的国际化形式,请参阅 RFC 6531
  • 主机名
  • IP 地址
  • 资源标识符
    • “uri”:通用资源标识符(URI),根据 RFC3986
    • “uri-reference”:URI 参考(URI 或相对参考), 参考 RFC3986, section 4.1
  • URI 模板
    • “uri-template”:URI 模板(任何级别),参考 RFC6570,如果您尚不知道 URI 模板是什么,则可能不需要此值。
  • JSON 指针
    • “json-pointer”:根据 RFC6901,在构造复杂的模式时,会更多地讨论 JSON Schema 中 JSON 指针 的使用。请注意,仅当整个字符串仅包含 JSON 指针内容时,才应使用此属性 /foo/bar。JSON 指针 URI 片段,例如 #/foo/bar/ 应使用 “uri-reference”。
    • “relative-json-pointer”:相对 JSON 指针。
  • 正则表达式
    • “regex”:正则表达式,参考 ECMA 262
      1. {
      2. "type": "string",
      3. "pattern": "^(\\([0-9]{3}\\))?[0-9]{3}-[0-9]{4}$"
      4. }

数值类型(Numeric types)

  • 整数: integer 用于定义整数类型。
    • { “type”: “integer” }。
    • 基于 JavaScript 的验证器可以接受 1.0 为整数,而基于其他语言可能不接受,可以巧妙地使用 multipleOf 关键字 { “type”: “number”, “multipleOf”: 1.0 }。
  • 数字: number 用于定义任何数字类型,整数或浮点数。
    • { “type”: “number” }
  • 倍数:multipleOf 可以将数字限制为给定数字(任何正数)的倍数。
    • {“type”: “number”, “multipleOf” : 10 }
  • 区间(范围):使用 minimum 和 maximum 关键字组合来指定数字范围(或用 exclusiveMinimum 与 exclusiveMaximum 指定单独范围)。
    • x ≥ minimum,指定 minimum 最小值且包含该值。
    • x > exclusiveMinimum,指定 exclusiveMinimum 最小值不包含该值。
    • x ≤ maximum,指定 maximum 最大值且包含该值。
    • x < exclusiveMaximum,指定 exclusiveMaximum 最大值不包含该值。

对象(object)

属性(Properties)

使用 properties 关键字定义对象上的属性(键值对),值为一个对象。

  1. {
  2. "type": "object",
  3. "properties": {
  4. "number": { "type": "number" },
  5. "street_name": { "type": "string" },
  6. "street_type": {
  7. "type": "string",
  8. "enum": ["Street", "Avenue", "Boulevard"]
  9. }
  10. }
  11. }

必须属性(Required Properties)

  1. {
  2. "type": "object",
  3. "properties": {
  4. "name": { "type": "string" },
  5. "email": { "type": "string" },
  6. "address": { "type": "string" },
  7. "telephone": { "type": "string" }
  8. },
  9. "required": ["name", "email"]
  10. }

属性个数(Size)

  1. {
  2. "type": "object",
  3. "minProperties": 2,
  4. "maxProperties": 3
  5. }

属性依赖(Property dependencies)

在下面的示例中,每当提供信用卡 credit_card 属性时,账单 billing_address 也必须存在:

  1. {
  2. "type": "object",
  3. "properties": {
  4. "name": { "type": "string" },
  5. "credit_card": { "type": "number" },
  6. "billing_address": { "type": "string" }
  7. },
  8. "required": ["name"],
  9. "dependencies": {
  10. "credit_card": ["billing_address"]
  11. }
  12. }

但依赖性不是双向的,即数据只存在账单 billing_address 不存在信用卡 credit_card 也是正确的。当然可以定义双向依赖性:

  1. {
  2. "type": "object",
  3. "properties": {
  4. "name": { "type": "string" },
  5. "credit_card": { "type": "number" },
  6. "billing_address": { "type": "string" }
  7. },
  8. "required": ["name"],
  9. "dependencies": {
  10. "credit_card": ["billing_address"],
  11. "billing_address": ["credit_card"]
  12. }
  13. }

附加属性(Asdditional Properties)

使用 additionalPropertiesis 定义额外的属性,值为 false 或 schema 的对象。

例如下面的例子,允许要附加属性,但类型必须是 string

  1. {
  2. "type": "object",
  3. "properties": {
  4. "number": { "type": "number" },
  5. "street_name": { "type": "string" },
  6. "street_type": {
  7. "type": "string",
  8. "enum": ["Street", "Avenue", "Boulevard"]
  9. }
  10. },
  11. "additionalProperties": { "type": "string" }
  12. }

正则属性(Pattern Properties)

properties、 patternProperties 、additionalPropertiesis 结合使用时为并集

  1. {
  2. "type": "object",
  3. "properties": {
  4. "builtin": { "type": "number" }
  5. },
  6. "patternProperties": {
  7. "^S_": { "type": "string" },
  8. "^I_": { "type": "integer" }
  9. },
  10. "additionalProperties": { "type": "string" }
  11. }

数组(array)

元素(items)

默认情况下,数组的元素可以是任何东西。但也可以根据使用 items, additionalItems 和contains 关键字验证数组的元素。 JSON 数组通常有两种方式:

  • 列表验证(List validation): 任意长度的序列,其中每个项目都匹配相同的模式(schema)。
  • 元组验证(Tuple validation): 固定长度的序列,其中每个项目可能具有不同的模式(schema)。

列表验证(List validation)

  • 每个项目都匹配相同模式,items 关键字设置为单个模式,验证数组中的所有项目,空数组也符合。
  • 当 items 为单个模式(为一个对象)时,additionalItems 关键字是没有意义的,因此不应使用。

如下,所有元素都为数字:

  1. {
  2. "type": "array",
  3. "items": {
  4. "type": "number"
  5. }
  6. }

contains 模式仅需要针对数组中的一个或多个项目进行验证。如下,只需包含至少一个数字元素:

  1. {
  2. "type": "array",
  3. "contains": {
  4. "type": "number"
  5. }
  6. }

长度(Length)

  1. {
  2. "type": "array",
  3. "minItems": 2,
  4. "maxItems": 3
  5. }

唯一(Uniqueness)

  1. {
  2. "type": "array",
  3. "uniqueItems": true
  4. }

布尔(boolean)

  1. { "type": "boolean" }

通用关键字(Generic keywords)

注释(Annotations)

  1. {
  2. "title": "Match anything",
  3. "description": "This is a schema that matches anything.",
  4. "default": "Default value",
  5. "examples": ["Anything", 4035]
  6. }

枚举值(Enumerated values)

enum 关键字被用于限制值,以一个固定的一组值。它必须是一个至少包含一个元素的数组,其中每个元素都是唯一的。且 type 和 enum 是交集,必须同时满足。

  1. {
  2. "type": "string",
  3. "enum": ["red", "amber", "green"]
  4. }

常量值(Constant values)

const 是 enum 的语法糖。下面两个定义是等价的。

  1. { "const": "United States of America" }
  2. { "enum": [ "United States of America" ] }

结合模式(Combining schemas)

JSON 模式包括一些用于将模式组合在一起的关键字。

  • allOf 必须对所有子方案有效
  • anyOf 必须是有效对抗任何的子模式的
  • oneOf 必须确保有效的只有一个的子模式的
  • not 必须不能是对给定的模式是有效的,即非在声明规则

allOf,anyOf,oneOf 关键字都必须设置为一个数组,其中每个项目都是独立一个定义,即数组中列出的模式彼此之间一无所知。

所有的(allOf)

要针对进行验证 allOf,给定的数据必须对所有给定的子方案都有效。且子方案有交集。

  1. {
  2. "allOf": [{ "type": "string" }, { "maxLength": 5 }]
  3. }

任何(anyOf)

验证 anyOf,给定的数据必须针对任何(一个或多个)给定的子方案有效。并集。

  1. {
  2. "anyOf": [{ "type": "string" }, { "type": "number" }]
  3. }

一个(oneOf)

验证 oneOf,给定的数据必须对给定的子方案之一有效。有且只能满足一个条件。

  1. {
  2. "oneOf": [
  3. { "type": "number", "multipleOf": 5 },
  4. { "type": "number", "multipleOf": 3 }
  5. ]
  6. }
  7. {
  8. "oneOf": [
  9. { "required": ["name","age"]},
  10. { "required": ["name","addr"]}
  11. ]
  12. }

不(not)

not 关键字声明一个实例进行验证,要求验证的数据它不符合定义的子模式。如下例,非字符串类型:

  1. { "not": { "type": "string" } }

有条件地应用子模式

if,then 和 else 关键字。规则为如果 if 有效,则 then 也必须有效(并被 else 忽略。)如果 if 无效,则 else 必须有效(并被then忽略)。

例如定义不同地方的邮政编码:如果地址在美国,则 postal_code 字段为五个数字后跟一个可选的四位后缀。如果地址在加拿大,则该 postal_code字段为六位字母数字字符串,字母和数字交替出现。

  1. {
  2. "type": "object",
  3. "properties": {
  4. "street_address": {
  5. "type": "string"
  6. },
  7. "country": {
  8. "enum": ["United States of America", "Canada"]
  9. }
  10. },
  11. "if": {
  12. "properties": { "country": { "const": "United States of America" } }
  13. },
  14. "then": {
  15. "properties": { "postal_code": { "pattern": "[0-9]{5}(-[0-9]{4})?" } }
  16. },
  17. "else": {
  18. "properties": {
  19. "postal_code": { "pattern": "[A-Z][0-9][A-Z] [0-9][A-Z][0-9]" }
  20. }
  21. }
  22. }

$schema 关键字

$schema 关键字被用于声明一个 JSON 片段。它还声明了针对该架构编写的 JSON Schema 标准版本。建议所有 JSON 模式都有一个 $schema 条目,该条目必须位于根目录下。

  1. "$schema": "http://json-schema.org/schema#"

构建复杂的模式

重用

复用部分建议将其放在父架构下的键 definitions 下。

  1. {
  2. "definitions": {
  3. "address": {
  4. "type": "object",
  5. "properties": {
  6. "street_address": { "type": "string" },
  7. "city": { "type": "string" },
  8. "state": { "type": "string" }
  9. },
  10. "required": ["street_address", "city", "state"]
  11. }
  12. }
  13. }

$ref 属性

$ref 关键字从其他地方引用此模式片段。值为是 URI 的引用,# 符号(“片段”或“命名锚”)后的部分为JSON Pointer的格式。

  1. { "$ref": "#/definitions/address" }

如果您使用的是同一文档中的定义,则该 $ref 值以井号(#)开头。之后,以斜杠分隔的项目遍历文档中对象中的键。因此,在我们的示例中 “#/definitions/address” 意味着:

  • 转到该文档的根
  • 找到 “definitions” 键的值。
  • 在该对象中,找到键 “address” 的值

$ref 可以解析为引用另一个文件的 URI,因此,如果您希望将定义包含在单独的文件中,也可以这样做。例如:

  1. { "$ref": "definitions.json#/address" }

所以完整事例为:

  1. {
  2. "$schema": "http://json-schema.org/draft-07/schema#",
  3. "definitions": {
  4. "address": {
  5. "type": "object",
  6. "properties": {
  7. "street_address": { "type": "string" },
  8. "city": { "type": "string" },
  9. "state": { "type": "string" }
  10. },
  11. "required": ["street_address", "city", "state"]
  12. }
  13. },
  14. "properties": {
  15. "billing_address": { "$ref": "#/definitions/address" },
  16. "shipping_address": { "$ref": "#/definitions/address" }
  17. }
  18. }

递归

$ref 元素可用于创建引用自己的递归模式。例如,您可能有一个 person 架构,其中包含一个数组 children,每个数组也是 person 实例。

  1. {
  2. "$schema": "http://json-schema.org/draft-07/schema#",
  3. "definitions": {
  4. "person": {
  5. "type": "object",
  6. "properties": {
  7. "name": { "type": "string" },
  8. "children": {
  9. "type": "array",
  10. "items": { "$ref": "#/definitions/person" },
  11. "default": []
  12. }
  13. }
  14. }
  15. },
  16. "type": "object",
  17. "properties": {
  18. "person": { "$ref": "#/definitions/person" }
  19. }
  20. }

$id 属性

$id属性是一个 URI 引用,有两个用途:

  • 它为模式声明了唯一的标识符。
  • 它声明了一个基本 URI,$ref 可以针对该基本 URI 引用进行解析。

    1. {
    2. "$id": "https://example.com/schemas/customer",
    3. "type": "object",
    4. "properties": {
    5. "first_name": { "type": "string" },
    6. "last_name": { "type": "string" },
    7. "shipping_address": { "$ref": "/schemas/address" },
    8. "billing_address": { "$ref": "/schemas/address" }
    9. },
    10. "required": ["first_name", "last_name", "shipping_address", "billing_address"]
    11. }

    上面的例子 { “$ref”: “/schemas/address” } == { “$ref”: “https://example.com/schemas/address“ }

扩展

$ref 真正的亮点在于使用 allOf,anyOf 和 oneOf 组合的时候。

比如我们定义了一个基础的类型,我们可以使用allOf 去扩展这个类型

  1. {
  2. "$schema": "http://json-schema.org/draft-07/schema#",
  3. "definitions": {
  4. "address": {
  5. "type": "object",
  6. "properties": {
  7. "street_address": { "type": "string" },
  8. "city": { "type": "string" },
  9. "state": { "type": "string" }
  10. },
  11. "required": ["street_address", "city", "state"]
  12. }
  13. },
  14. "type": "object",
  15. "properties": {
  16. "billing_address": { "$ref": "#/definitions/address" },
  17. "shipping_address": {
  18. "allOf": [
  19. { "$ref": "#/definitions/address" },
  20. {
  21. "properties": { "type": { "enum": ["residential", "business"] } },
  22. "required": ["type"]
  23. }
  24. ]
  25. }
  26. }
  27. }