JSON schema(模式)

 
JSON Schema 是一个描述和验证 JSON 数据结构的强大工具,我们可以把 JSON Schema 看作是一种规范,这个规范中规定了 JSON 数据的结构、键的命名、值的类型等等,通过规范可以校验指定的 JSON 数据,保证数据的准确。所以在接口调试过程中,经常使用 JSON Schema 来校验接口数据的准确性。

什么是 JSON Schema

JSON Schema 译为“JSON模式”,它是由 IETF 编写和起草的。那么 JSON Schema 有什么作用呢?

IETF 是 The Internet Engineering Task Force 的简称,译为“国际互联网工程任务组”,是一个公开性质的大型民间国际团体。

假如我们要使用 JSON 来存储一个产品的信息,如下所示:
{
    "id": 1,
    "name": "JSON教程",
    "author": "C语言中文网",
    "price": 99.9
}
如上所示,虽然只是一段很简单的 JSON 数据,但是有人可能会问:“id”代表什么意思、“name”字段是否可以省略、“price”字段的值是否可以为 0 等等。

JSON Schema 就是为了解决上述问题诞生的,在 JSON Schema 中可以规定要在 JSON 中出现的字段、字段含义、字段类型等信息,而且可以使用 JSON Schema 来校验 JSON 数据是否符合 Schema 中定义的规范。

定义 Schema

JSON Schema 本身是一段 JSON 格式的数据,如下例所示:
{
    "$schema": "http://json-schema.org/draft-04/schema#",
    "type": "object",
    "properties": {
        "name":  { "type": "string" },
        "email": { "type": "string" },
        "age": {
            "type": "number",
            "minimum": 0,
            "exclusiveMinimum": false
        },
        "telephone": {
            "type": "string",
            "pattern": "^(\\([0-9]{3}\\))?[0-9]{3}-[0-9]{4}$"
        }
    },
    "required": ["name", "email"],
    "additionalProperties": false
}
上面 Schema 中,要求 JSON 数据必须符合以下要求:
  • 类型为 object;
  • JSON 中可以包含以下四个键:
    • name:必须是字符串类型;
    • email:必须是字符串类型;
    • age:必须是大于 0 的整数;
    • telephone:必须是字符串类型,并且需要匹配指定的正则表达式。
  • 上面的四个键中,name 和 email 必须定义,而 age 和 telephone 则是可选的;
  • 除了上面四个键,不允许出现其它的键。

下面就是一个符合上面 Schema 的 JSON 数据:
{
    "name": "C语言中文网",
    "email": "2758010091@qq.com",
    "age": 18
}
大家都知道 JSON 中支持 string、number、object、array、boolean、null 等几种类型,针对不同的类型,Schema 中提供了一系列关键字,下面我们来介绍一下这些关键字以及用法:

1) 字符串(String)

JSON Schema 中有关字符串的关键字如下表所示:

关键字 描述 可选值
minLength 字符串最小长度,不能为负数  
maxLength 字符串最大长度,不能为负数  
pattern 正则表达式  
format 字符串格式 "date-time":日期和时间,如 2018-11-13T20:20:39+00:00
"time":时间,如 20:20:39+00:00
"date":日期,如 2018-11-13
"email":电子邮箱地址
"hostname":主机名
"ipv4":IPv4 地址
"ipv6":IPv6 地址
"uri":通用资源标识符(URI)
"uri-template":URI 模板(任何级别)
"regex":正则表达式

2) 数值类型

JSON Schema 中有关数值的关键字如下表所示:

关键字 描述 示例
integer 整数 {"type": "integer"}
number 数字,包括整数和浮点数 {"type": "number"}
multipleOf 数字必须是给定数字(正数)的倍数 {"type": "number", "multipleOf": 3}
minimum 数值允许的最小值 {"type": "number", "minimum": 1}
maximum 数值允许的最大值 {"type": "number", "maximum": 9}

3) 对象

属性(Properties)

在 JSON Schema 中,您可以使用 properties 关键字来定义 JSON 数据中的键,如下例所示:
{
    "type": "object",
    "properties": {
        "name": { "type": "string"},
        "age": {"type": "number", "multipleOf": 3},
        "date": {
            "type": "string",
            "format": "date"
        }
    }
}

额外属性(Asdditional Properties)

在 JSON Schema 中,您可以使用 additionalPropertiesis 定义额外的属性,值为 false 或一个 Schema 对象,如下例所示
{
    "type": "object",
    "properties": {
        "number": { "type": "number"},
        "street_name": { "type": "string"},
        "street_type": {
            "type": "string",
            "enum": ["Street", "Avenue", "Boulevard"]
        }
    },
    "additionalProperties": { "type": "string"}
}

必须属性(Required Properties)

在 JSON Schema 中,您可以使用 required 关键字声明 JSON 中必须定义的键,required 的值为一个数组,数组中的每个值必须是唯一的,如下例所示:
{
    "type": "object",
    "properties": {
        "name": { "type": "string" },
        "email": { "type": "string" },
        "address": { "type": "string" },
        "telephone": { "type": "string" }
    },
    "required": ["name", "email"]
}

属性名称(Property names)

在 JSON Schema 中,您可以使用 propertyNames 定义 JSON 数据中键的命名规则,如下例所示:
{
    "type": "object",
    "propertyNames": {
        "pattern": "^[A-Za-z_][A-Za-z0-9_]*$"
    }
}

属性个数(Size)

在 JSON Schema 中,您可以使用 minProperties(最小数量)和 maxProperties(最大数量)关键字来限制 JSON 对象中键的个数,如下例所示:
{
    "type": "object",
    "minProperties": 2,
    "maxProperties": 3
}

4) 数组

数组是值的有序集合,JSON 数组中的每个值都可以是不同的类型。

元素(items)

默认情况下,数组的值可以是任何类型,但在 JSON Schema 中可以使用 items、additionalItems 和 contains 关键字来验证数组中的值,对于 JSON 中的数组,通常有两种验证方式:
  • 列表验证(List validation):任意长度的数组,数组中每个值都匹配相同的 Schema;
  • 元组验证(Tuple validation):固定长度的数组,数组中每个值可以匹配不同的 Schema。

列表验证(List validation)

若希望数组中每个值都匹配相同的模式,可以通过 items 关键字设置单个模式,来验证数组中的所有值。当使用单个模式时,additionalItems 关键字是无效的,如下例所示(验证数组的所有值为数字):
{
    "type": "array",
    "items": {
        "type": "number"
    }
}
contains 关键字仅需要针对数组中的一个或多个项目进行验证,如下例所示(只需包含至少一个数字元素):
{
    "type": "array",
    "contains": {
        "type": "number"
    }
}

元组验证(Tuple validation)

在 JSON 数据中,如果数组的每个值都有不同的含义,那么这些值的类型也可能是不同的,例如:

[编号, 街道名称, 街道类型, 方向]

为此,在 Schema 中我们可以向下面这样来验证 JSON 数据:
{
    "type": "array",
    "items": [
        {
            "type": "number"
        },
        {
            "type": "string"
        },
        {
            "type": "string",
            "enum": ["Street", "Avenue", "Boulevard"]
        },
        {
            "type": "string",
            "enum": ["NW", "NE", "SW", "SE"]
        }
    ]
}

数组长度(Length)

可以使用 minItems、 maxItems 关键字指定数组的长度。如下例所示(指定数组的长度范围为 2 ~ 3):
{
    "type": "array",
    "minItems": 2,
    "maxItems": 3
}

注意:数组长度不能为负数。

唯一(Uniqueness)

将 uniqueItems 关键字设置为 true 可以确保数组中的每个元素都是唯一的,如下例所示:
{
    "type": "array",
    "uniqueItems": true
}

5) 布尔(boolean)

布尔类型只有两个值:true 和 false,如下例所示:

{"type": "boolean"}

6) 空值(null)

空类型通常用于表示没有值,当将值的类型指定为 null 时,则表示它只有一个可接受的值——null,如下例所示:

{"type": "null"}

7) 通用关键字

JSON Schema 包括几个通用的关键字 title、description、default、examples 用于不严格验证,这些关键字主要用来描述模式的功能、作用。这些注释属性都不是必须的,但建议添加。

  • title 和 description 关键字必须是字符串,“title”最好比较简短,而“description”应该提供关于该 schema 的详细描述;
  • default 关键字用来定义默认值;
  • examples 关键字是用于提供一系列针对该模式进行验证的示例。

如下例所示:
{
    "title": "Match anything",
    "description": "This is a schema that matches anything.",
    "default": "Default value",
    "examples": ["Anything", 4035]
}

注释(Comments)

$comment 关键字用于添加注释/批注,它的值必须是一个字符串。

枚举值(Enumerated values)

enum 关键字被用于定义枚举值(一组固定的值),它必须是一个至少包含一个值的数组,而且每个值都必须是唯一的。此外,type 和 enum 是并列的,必须同时满足,如下例所示:
{
    "type": "string",
    "enum": ["red", "amber", "green"]
}

常量值(Constant values)

const 关键字被用于定义一个常量值,如下例所示:
{
    "properties": {
        "country": {
            "const": "United States of America"
        }
    }
}
const 是 enum 的语法糖,所以下面两个定义是等价的:
{ "const": "United States of America" }
{ "enum": [ "United States of America" ]}

8) $schema 关键字

$schema 关键字用于声明 JSON Schema 的版本,建议所有 JSON Schema 都声明一个 $schema,并且应该是 Schema 的第一个键/值对,如下例所示:
{"$schema": "http://json-schema.org/draft-04/schema#"}
如果需要声明 JSON Schema 的指定版本,则可以将 $schema 的值设置为以下几个:
  • http://json-schema.org/draft-07/schema#
  • http://json-schema.org/draft-06/schema#
  • http://json-schema.org/draft-04/schema#

9) $ref 关键字

$ref 关键字用于引用其他地方的一个 Schema 片段,它的值为一个 URI 引用。如果是引用同一文档的 Schema 片段,则 $ref 的值需要以井号(#)开头,例如:

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

上面示例中的“#/definitions/address”意味着:
  • 转到文档的根;
  • 找到 "definitions" 键的值;
  • 在该对象中,找到键 "address" 的值。

如果要引用另外的 Schema 文件,可以向下面这样:

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

完整的示例如下所示:
{
    "$schema": "http://json-schema.org/draft-04/schema#",
    "definitions": {
        "address": {
            "type": "object",
            "properties": {
                "street_address": { "type": "string" },
                "city": { "type": "string" },
                "state": { "type": "string" }
            },
            "required": ["street_address", "city", "state"]
        }
    },
    "type": "object",
    "properties": {
        "billing_address": { "$ref": "#/definitions/address" },
        "shipping_address": { "$ref": "#/definitions/address" }
    }
}
使用 $ref 关键字也可以引用当前 Schema 中的某一部分(类似于编程中的递归)。例如您有一个 person 模式,其中包含一个数组 children,每个数组也是一个 person 实例,如下例所示:
{
    "$schema": "http://json-schema.org/draft-07/schema#",
    "definitions": {
        "person": {
            "type": "object",
            "properties": {
                "name": { "type": "string" },
                "children": {
                    "type": "array",
                    "items": { "$ref": "#/definitions/person" },
                    "default": []
                }
            }
        }
    },
    "type": "object",
    "properties": {
        "person": { "$ref": "#/definitions/person" }
    }
}

10) $id 关键字

$id 关键字的值是一个 URI 引用,它有两个用途:
  • 为 Schema 声明一个唯一的标识符;
  • 声明一个基本 URI,$ref 可以基于该 URI 进行引用。

例如我们要引用 http://c.biancheng.net/ 域名的某个 Schema,则可以将 $id 设置为下面这样:

{ "$id": "http://c.biancheng.net/schemas/address.json" }

注意,当定义了上面所示的 $id 之后,若我们要使用 $ref 来引用同域名下的其它 Schema,则可以简写成如下所示的样子:

{ "$ref": "person.json" }

使用 JSON Schema 进行验证

了解了 JSON Schema 之后,我们就可以使用它来校验 JSON 数据了。网络中有许多 JSON Schema 的库,您可以在 JSON Schema 的官网查找合适的库。

除了可以使用第三方库外,还可以通过一些在线工具来使用 Schema,例如 https://jsonschemalint.com/#!/version/draft-04/markup/json