JSON Schema 使用教程文档

兼做文档,随手翻。
整理来源:《Understanding JSON Schema》

1. JSON SCHEMA REFERENCE

type string

  • “type”: “string”
  • “maxLength”
  • “minLength”
  • “pattern”: “^(\([0-9]{3}\))?[0-9]{3}-[0-9]{4}$”
  • “format”
    • “date-time”: Date representation, as defined by RFC 3339, section 5.6.
    • “email”: Internet email address, see RFC 5322, section 3.4.1.
    • “hostname”: Internet host name, see RFC 1034, section 3.1.
    • “ipv4”: IPv4 address, according to dotted-quad ABNF syntax as defined in RFC 2673, section 3.2.
    • “ipv6”: IPv6 address, as defined in RFC 2373, section 2.2.
    • “uri”: A universal resource identifier (URI), according to RFC3986.

type number

  • type
    • “integer”
      —— 依赖 schema validator 实现,在不同的实现里可能会作为不同的判定。(For example, a JavaScript-based may accept 1.0 as an integer, whereas the Python-based jsonschema does not. 1.0)
    • “number”
  • “multipleOf”: 10
    —— 10的倍数
  • “minimun”: 10
    —— 最小是10
  • “exclusiveMinimum”: true
    —— 为true代表不包含10
  • “maximun”: 100
    —— 最大是100
  • “exclusiveMaximum”: false
    —— 或者未定义,包括100

type object —— key 必须是string

  • “type”: “object”
  • “properies”: {“number”: {“type”: “number”}}
    —— 默认缺失了属性和多了什么属性都是可以通过验证的,即{number: 1}和{}和{number: 1, a: 2}都符合规则;
  • “additionalProperties”:
    —— 如果没有添加额外的属性,这个属性实际不会作用
    • false
      —— 不允许有附加的多余属性
    • {“type”: “string”, “pattern”: “^(\([0-9]{3}\))?[0-9]{3}-[0-9]{4}$”}
      —— 值为object,默认所有添加上去的属性,都必须满足这个object的条件
  • “required”: [“name”, “tel”]
    —— 值为一个数组,指定properies哪些属性是必须的
  • “minProperties”: 2
    —— 最少有两个属性
  • “maxProperties”: 3
    —— 最多有三个属性
  • “dependencies”:

    • properies dependencies
      —— {“credit_card”: [“billing_address”]} 相当于依赖的管理,credit_card属性的存在,必须要有billing_address属性;甚至可以建立双向依赖

      1
      // valid
      {
          "name": "John Doe",
          "credit_card": 5555555555555555,
          "billing_address": "555 Debtor
      }
      
      // invalid
      {
          "name": "John Doe",
          "credit_card": 5555555555555555
      }
      
      // valid
      {
            "name": "John Doe"
      }
      
      // valid
      {
          "name": "John Doe",
          "billing_address": "555 Debtor
      }
      
      // 双向依赖
      {
          "type": "object",
          "properties": {
              "name": { "type": "string" },
              "credit_card": { "type": "number" },
              "billing_address": { "type": "string" }
          },
          "required": ["name"],
          "dependencies": {
              "credit_card": ["billing_address"],
              "billing_address": ["credit_card"]
          }
      }
    • Schema dependencies,相当于属性依赖的扩展;可以指定依赖到的属性的类型,以及需要满足的条件

      1
      "dependencies": {
          "credit_card": {
              "properties": {
                  "billing_address": { "type": "string" }
              },
              "required": ["billing_address"]
          }
      }
  • “patternProperties”
    —— 通过正则匹配属性;默认为全局搜索,例如”p”的搜索,”apple” 满足条件,要达到目的,需要是”^p$”;与additionalProperties配合食用效果更佳

1
"patternProperties": {
    "^S_": { "type": "string" },
    "^I_": { "type": "integer" }
}

type array

  • “type”: “array”
  • “items”

    • List validation,简单、全部满足的,每一项都满足相同的条件;additionalItems不要和这个一起用,无效

      1
      "items": {
          "type": "number"
      }
      
      // valid
      [1, 2, 3, 4, 5]
      
      // invalid
      [1, 2, "3", 4, 5]
      
      // valid 空数组也符合情况
      []
    • Tuple validation,针对元素在数组中的位置来校验不同规则

      1
      {
          "type": "array",
          "items": [
              {
                  "type": "number"
              },
              {
                  "type": "string"
              },
              {
                "type": "string",
                "enum": ["Street", "Avenue", "Boulevard"]
              }, 
              {
                "type": "string",
                "enum": ["NW", "NE", "SW", "SE"]
              }
          ]
      }
      
      // 只要满足元素项的规则
      
      // valid 三缺一,可以的
      [10, "Downing", "Street"]
      
      // valid 多来了一个也没事
      [1600, "Pennsylvania", "Avenue", "NW", "Washington"]
  • additionalItems: false —— 需要和Tuple validation配合食用,代表是否允许添加其他的数组项

1
{
    "type": "array",
    "items": [
        {
            "type": "number"
        },
        {
            "type": "string"
        },
        {
          "type": "string",
          "enum": ["Street", "Avenue", "Boulevard"]
        }, 
        {
          "type": "string",
          "enum": ["NW", "NE", "SW", "SE"]
        }
    ],
    "additionalItems": false
}

// valid 三缺一,可以的
[10, "Downing", "Street"]

// invalid 多来了一个? !!!就不让来!!!
[1600, "Pennsylvania", "Avenue", "NW", "Washington"]
  • “minItems”: 2
    —— 最小数组元素个数
  • “maxItems”: 3
    —— 最大数组元素个数
  • “uniqueItems”: true
    —— 数组元素必须是唯一不重复的
1
// valid
[1, 2, 3, 4, 5]
[]

// invalid
[1, 2, 3, 3, 4]

type boolean —— 只要配置一个,值只能是 boolean值 true false

  • “type”: “boolean”

type null —— 只要配置一个,值只能是 null;一般用于代表属性

  • “type”: “null”

2. Generic keywords

所有情况都能用的关键字。

Metadata

  • “title”: “标题”
  • “description”: “描述信息”
  • “default”: “空的属性的默认值”
1
{
  "title" : "Match anything",
  "description" : "This is a schema that matches anything.",
  "default" : "Default value"
}

Enumerated values

  • “enum”: [“red”, “amber”, “green”] —— 列举出来的内容里面出现一个
1
{
    "type": "string",
    "enum": ["red", "amber", "green"]
}

 {
    "enum": ["red", "amber", "green", null, 42]
}

注意: 会先匹配type,才匹配enum,例如:

1
{
    "type": "string",
    "enum": ["red", "amber", "green", null]
}

// invalid
null

// valid
"red"

3. Combining schemas

allOf 全部满足

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

// valid
"short"

// invalid
"too long"

吊吊的写法:

1
{
    "definitions": {
        "address": {
            "type": "object",
            "properties": {
                "street_address": { "type": "string" },
                "city":           { "type": "string" },
                "state":          { "type": "string" }
            },
            "required": ["street_address", "city", "state"]
        }
    },
    "allOf": [
        { 
            // $ref 也是有点吊的,好比继承和模块化
            "$ref": "#/definitions/address" 
        },
        { 
            "properties": {
                "type": { "enum": [ "residential", "business" ] }
            }
        }
    ]
}

// valid
{
    "street_address": "1600 Pennsylvania Avenue NW",
    "city": "Washington",
    "state": "DC",
    "type": "business"
}

注意事项:

1
{
    "definitions": {
        "address": {
            "type": "object",
            "properties": {
                "street_address": { "type": "string" },
                "city": { "type": "string" },
                "state": { "type": "string" }
            },
            "required": ["street_address", "city", "state"]
        }
    },
    "allOf": [
        { 
            "$ref": "#/definitions/address"
        },
        {
            "properties": {
                "type": { "enum": [ "residential", "business" ] }
            }
        }
    ],
    "additionalProperties": false
}

// invalid
// 因为在一开始,properties不是在根下的,allOf的处理,并没有在一开始将具体项的properties合并起来
// 因此,上面的schema,因为additionalProperties,只能接受 [] 空对象;又因为type enum,导致无论什么都无效
{
    "street_address": "1600 Pennsylvania Avenue NW",
    "city": "Washington",
    "state": "DC",
    "type": "business"
}

anyOf 只要满足其中任意一个

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

oneOf 只能满足其中任意一个

1
{
    "oneOf": [
        { "type": "number", "multipleOf": 5 },
        { "type": "number", "multipleOf": 3 }
    ]
}

// valid  5   3
// invalid 15

// 抽离公用、重复情况 "type": "number"
// 仅仅就写个schema都有公用、避免重复的想法在里面,写那么多年JS、写PHP,一点封装的意识都没有,怎么都不会羞愧。
{
    "type": "number",
    "oneOf": [
        { "multipleOf": 5 },
        { "multipleOf": 3 }
    ]
}

not 不是不是就不是

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

4. The $schema keyword

代表这是一个schema,并带上版本,并且做好在根那一级带上这个属性。

当前貌似都在用 http://json-schema.org/draft-04/schema# 这个版本。

5. STRUCTURING A COMPLEX SCHEMA

When writing computer programs of even moderate complexity, it’s commonly accepted that “structuring” the program into reusable functions is better than copying-and-pasting duplicate bits of code everywhere they are used. Likewise in JSON Schema, for anything but the most trivial schema, it’s really useful to structure the schema into parts that can be reused in a number of places. —— 《Understanding JSON Schema》CHAPTER 5 STRUCTURING A COMPLEX SCHEMA

简明扼要,写可复用的代码、写可复用的JSON-schema,拒绝重复。

使用 $ref

1
{ "$ref": "#/definitions/address" }
// # 为根, / 为路径
// 1. 到schema root
// 2. 找到"definitions"
// 3. 找到"address"的值,作为schema

{ "$ref": "definitions.json#/address" }
// 也可以是绝对路径,相对路径

加上id 属性

  • 作为这个schema的唯一识别装置
  • 提供一个基本路径给 $ref 用
1
{
    "id": "http://foo.bar/schemas/address.json"
    // 路径基于 http://foo.bar/schemas/
    // 等同于 http://foo.bar/schemas/person.json
    "$ref": "person.json" 
}
  • 将 $ref 与 allOf、anyOf、oneOf 配合使用
1
{
    "$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": {
            "allOf": [
                { "$ref": "#/definitions/address" },
                { "properties":
                    { "type": { "enum": [ "residential", "business" ] } },
                    "required": ["type"]
                }
            ]
        }
    }
}