json-schema 学习记录

229 阅读2分钟

背景

随着 json 越来越广泛地被应用在各处,特别地,对于 json 的规范定义和校验也变得十分重要:

  • 可以明确 json 中每个字段的含义、类型、约束条件之类的规则
  • 减少团队开发之间的理解成本、后续维护成本、新人熟悉上手成本等
  • 对于校验json提供天然的规则支持

可以理解为,json-schema 的出现,就是为了解决如何使用统一规则来定义和校验 json的。

定义

它是一套用于校验json的规范,让读写都同时遵循的一套规则。

json-schema之于json,就如同typescript(或flow)之于javascript

说明图片

基本使用

基本概念

基本概念图

常用属性

  • maxLength: 最大长度限制

  • enum: 属性值的可用枚举值

  • minimum、exclusiveMinimum:大于等于、大于

  • maximum、exclusiveMaximum:小于等于、小于

  • pattern: 字符串约束支持正则表达式的描述

  • items: 规定数组的每一项;如果该属性是对象,就是properties

  • minItems: 至少必须xxx个元素以上

  • uniqueItems: 规定每个元素必须是唯一的(true/false)

例子

一个 json-schema 定义:

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "http://example.com/person.schema.json",
  "title": "Person",
  "description": "it is a person object",
  "type": "object",
  "properties": {
    "id": {
      "description": "The unique identifier for a person",
      "type": "string"
    },
    "name": {
      "description": "The identifier for a person",
      "type": "string",
      "maxLength": 50
    },
    "sex": {
      "description": "The gender of the person",
      "type": "string",
      "enum": ["male", "female"]
    },
    "age": {
        "description": "The age for a person",
        "type": "number",
        "exclusiveMinimum": 0,
        "maximum": 1000
    },
    "phone": {
        "description": "The contact for a person",
        "type": "string",
        "pattern": "^[13|14|15|16|17|18|19][0-9]{9}$"
    },
    "tags": {
        "description": "The labels to describe a person",
        "type": "array",
        "items": [
            { "type": "string" }
        ],
        "minItems": 1,
        "uniqueItems": true
    },
    "address": {
        "description": "The address for a person",
        "type": "object",
        "properties": {
            "country": {
                "type": "string"
            },
            "province": {
                "type": "string"
            },
            "city": {
                "type": "string"
            },
            "region": {
                "type": "string"
            },
            "detail": {
                "type": "string"
            }
        },
        "required": ["country", province", "city", "region"]
    },
  },
  "required": [ "id", "name" ]
}

符合上述 schema的 json:

{
    "id": "A000000000",
    "name": "溥仪",
    "sex": "male",
    "age": 112,
    "phone": "1300000000",
    "tags": ["吃饭","睡觉","发呆","末代皇帝"],
    "address": {
        "country": "天朝",
        "province": "帝都",
        "city": "帝都",
        "region": "东城区",
        "detail": "景山前街4号故宫博物馆",
    }
}

进阶用法

复用

允许使用一些相同和类似的schema结构片段

使用 refref、id、结构路径来进行复用

例子1:

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "definitions": {
      "address": {
        "type": "object",
        "properties": {
            "country": {
                "type": "string"
            },
            "province": {
                "type": "string"
            },
            "city": {
                "type": "string"
            },
            "region": {
                "type": "string"
            },
            "detail": {
                "type": "string"
            }
        },
        "required": ["country", province", "city", "region"]
    },
  },
  
  "type": "object",
  
  "properties": {
        "receipt_address": {
            "#ref": "#/definitions/address"
        },
        "send_address": {
            "#ref": "#/definitions/address"
        }
  }
}

例子2:

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "definitions": {
      "address": {
        "$id": "#address",
        "type": "object",
        "properties": {
            "country": {
                "type": "string"
            },
            "province": {
                "type": "string"
            },
            "city": {
                "type": "string"
            },
            "region": {
                "type": "string"
            },
            "detail": {
                "type": "string"
            }
        },
        "required": ["country", province", "city", "region"]
    },
  },
  
  "type": "object",
  
  "properties": {
        "receipt_address": {
            "#ref": "#address"
        },
        "send_address": {
            "#ref": "#address"
        }
  }
}

递归

既然 可以通过 $ref 实现复用,那么复用自己(递归)也是可行的

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "definitions": {
      "element": {
        "$id": "#element",
        "type": "object",
        "properties": {
            "name": {
                "type": "string"
            },
            "props": {
                "type": "object",
                "properties": {},
            },
            "children": {
                "type": "array",
                "items": {"$ref": "#element"},
            },
        }
    },
  },
  
  "type": "object",
  
  "properties": {
        "element": {
            "#ref": "#element"
        }
  }
}

实际应用

@formily/antd 应用

{
  "type": "object",
  "properties": {
    "aaa": {
      "key": "aaa",
      "name": "aaa",
      "type": "string",
      "title": "字段1",
      "x-component": "input"
    },
    "bbb": {
      "key": "bbb",
      "name": "bbb",
      "type": "number",
      "title": "字段2",
      "x-component": "numberpicker"
    },
    "ccc": {
      "key": "ccc",
      "name": "ccc",
      "type": "date",
      "title": "字段3",
      "x-component": "datepicker"
    }
  }
}

上述 json-schema中,包含了 校验属性(type)和业务属性(x-component),不算严格意义上的标准schema,算是伪schema,但是大致思路和设计都是一致的。

参考文章

官方属性规范文档

得物技术浅谈json-schema入门教程