Json Schema简介

1,733 阅读3分钟

[top]

介绍

Json Schema定义了一套词汇和规则,这套词汇和规则用来定义Json元数据,且元数据也是通过Json数据形式表达的。Json元数据定义了Json数据需要满足的规范,规范包括成员、结构、类型、约束等

eg:
{ 
    "city":"chicago",
    "number": 20,
    "user" : { 
        "name":"Alex",
        "age":20 
     } 
 }

在上面的例子中,web api要求提供city,number,user三个成员,其中city是字符串,number是数值,user是一个对象,又包含了name和age两个成员

对应的Json Schema如下:
{ 
    "type": "object",
    "properties": {
        "city": { "type": "string" },
        "number": { "type": "number" },
        "user": { 
            "type": "object",
            "properties": {
                "name" : {"type": "string"},
                "age" : {"type": "number"}
            }                       
        }
    }
}

关键字

type 类型

1.String

1.字符串长度 **关键字: minLength, maxLength** 对字符串的最小长度、最大长度做规范

eg:
{ "type" : "string", "minLength" : 2, "maxLength" : 3, }
  1. 正则表达式**关键字: pattern**
eg:
{ "type" : "string", "pattern" : "^(\\([0-9]{3}\\))?[0-9]{3}-[0-9]{4}$", }
  1. 字符串Format **关键字: format**用于电子邮件、日期、域名等
{ "type" : "string", "format" : "date", }
2.Object
  • Json对象是最常见的Json数据类型,合法的数据可以是
{ 
    "name": "Froid", 
    "age" : 26, 
    "address" : { "city" : "New York", "country" : "USA" } 
}
  • 成员的Schema**关键字:properties**
{ 
    "type": "object",      
    "properties": {      
        "name": {"type" : "string"}, 
        "age" : {"type" : "integer"
     }, 
     "address" : { 
         "type" : "object", 
         "properties" : { 
             "city" : {"type" : "string"}, 
             "country" : {"type" : "string"} 
         } 
       } 
    } 
}

对于上例中的Schema,合法的data是
{ 
    "name": "Froid", 
    "age" : 26, 
    "address" : { 
        "city" : "New York", 
        "country" : "USA" 
    } 
}
  • 批量定义成员Schema **关键字:patternProperties**
eg: {"S_1" : "abc"}
    {"S_1" : "abc", "I_3" : 1}
{ 
    "type": "object", 
    "patternProperties": { 
        "^S_": { "type": "string" }, 
        "^I_": { "type": "integer" } 
    } 
}
  • 必须出现的成员 **关键字:required**
{ 
    "type": "object",      
    "properties": {      
        "name": {"type" : "string"}, 
        "age" : {"type" : "integer"}, 
    }, 
    "required" : ["name"] 
}

上例中"name"成员是必须的,因此合法的数据可以是
{"name" : "mary", "age" : 26}
{"name""mary"}

但缺少"name"则是非法的
{"age" : 26}
  • 成员依赖关系**关键字:dependencies**
规定某些成员的依赖成员,不能在依赖成员缺席的情况下单独出现,属于数据完整性方面的约束。
{ 
    "type": "object", 
    "dependencies": { 
        "credit_card": ["billing_address"] 
    } 
}
dependencies也是一个字典结构,key是Json数据的属性名,value是一个数组,数组内也是Json数据的属性名,表示key必须依赖的其他属性

上面Json Schema合法的数据可以是
{}
{"billing_address" : "abc"}

但如果有"credit_card"属性,则"billing_address" 属性不能缺席。下面的数据是非法的
{"credit_card": "7389301761239089"}
  • 是否允许额外属性**关键字:additionaProperties**
规定object类型是否允许出现不在properties中规定的属性,只能取true/false
{ 
    "type": "object",      
    "properties": {      
        "name": {"type" : "string"}, 
        "age" : {"type" : "integer"}, 
    }, 
    "required" : ["name"], 
    "additionalProperties" : false 
}

上例中规定对象不能有"name""age"之外的成员。
合法的数据
{"name" : "mary"}
{"name" : "mary", "age" : 26}
非法的数据
{"name" : "mary", "phone" : ""84893948}

  •  属性个数的限制**关键字:minProperties, maxProperties**规定最少、最多有几个属性成员
{ 
    "type": "object", 
    "minProperties": 2, 
    "maxProperties": 3 
}
合法数据
{"name" : "mary", "age" : 26}
{"name" : "mary", "age" : 26, "phone" : "37839233"}
3.Number

1.number的合法数值可以是 2或0.1 而 integer只能是整数
2.**关键字:multipleOf**

要求数值必须是10的整数倍
{ "type" : "number", "multipleOf" : 10, }

3.**关键字: minimum, maximum, exclusiveMinimum, exclusiveMaximum**可以限制数值的方位,包括最大值、最小值、开区间最大值、开区间最小值

要求数值在[0, 100)范围内:
{ 
    "type" : "number", 
    "minimum": 0, 
    "exclusiveMaximum": 100 
}
4.Integer 要求数据必须是整数
5.Array

1.Json数组合法数据

[1, 2, 3]
[1, "abc", {"name" : "alex"}]
[]

2.数组的类型特定参数,可以用来限制成员类型是否允许额外成员最小元素个数最大元素个数是否允许元素重复

  • 数组成员类型 **关键字: items**
1.可以要求数组内每个成员都是某种类型,通过关键字items实现
{ 
    "type": "array", 
    "items": { 
        "type": 
        "number" 
    } 
}
2.关键字items还可以对应一个数组,这时Json数组内的元素必须与Json Schema内items数组内的每个Schema按位置一一匹配  
    eg: [1, "abc"]
{ 
    "type": "array", 
    "items": [ 
        {"type": "number" }, 
        { "type": "string" }
    ] 
}
  • 数组是否允许额外成员 **关键字: additionalItems**
当使用了items关键字,并且items关键字对应的是Schema数组,这个限制才起作用。
关键字additionalItems规定Json数组内的元素,除了一一匹配items数组内的Schema外,
是否还允许多余的元组。当additionalItems为true时,允许额外元素
eg:[1, "abc", "x"]
{ 
    "type": "array", 
    "items": [ 
        { "type": "number" }, 
        { "type": "string" }
    ], 
    "additionalItems" : true 
}
  • 数组元素个数 **关键字: minItems, maxItems** 可以限制数组内元素的个数
eg:[1,2,3,4,5,6]
{ 
    "type": "array", 
    "items": { "type": "number" }, 
    "minItems" : 5, 
    "maxItems" : 10 
}
  • 数组内元素是否必须唯一 **关键字: uniqueItems**
eg:[1,2,3,4,5]
{ 
    "type": "array", 
    "items": { "type": "number" }, 
    "uniqueItems" : true 
}
6.Boolean
{"type" : "boolean"}
7.null
{"type" : "null"}
8.逻辑组合**关键字:allOf, anyOf, oneOf, not**
  1. 从关键字名字可以看出其含义,满足所有、满足任意、满足一个。前三个关键字的使用形式是一致的,以allOf为例说明其形式
{ 
    "allOf" : [ 
        Schema1, 
        Schema2, ... 
    ] 
}
其中,"allOf"的内容是一个数组,数组内的成员都是内嵌的Json Schema。上例Schema1Schema2都是内嵌的Json Schema。整个Schema表示当前Json数据,需要同时满足Schema1Schema2。

需要注意,不论在内嵌的Schema里还是外部的Schema里,都不应该使"additionalProperties"false。否则可能会生成任何数据都无法满足的矛盾Schema。
可以用来实现类似“继承”的关系,例如我们定义了一个Schema_base,如果想要对其进行进一步修饰,可以这样来实现。

{ 
    "allOf" : [ Schema_base ],
    "properties" : { 
        "other_pro1" : {"type" : "string"}, 
        "other_pro2" : {"type" : "string"} 
    }, 
    "required" : ["other_pro1", "other_pro2"] 
}
Json数据既需要满足Schema_base,又要具备属性"other_pro1""other_pro2"
  1.  关键字not 规定 Json不能满足not所对应的Schema
只要不是string类型的都Json数据都可以
{ "not" : {"type" : "string"} }

9.复杂结构

  • **关键字:无**
定义一个类型,并不需要特殊的关键字。通常的习惯是在root节点的definations下面,定义需要多次引用的schema。definations是一个json对象,key是想要定义的“类型”的名称,value是一个json 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" }
    }
}
上例中定义了一个address的schema,并且在两个地方引用了它,`#/definitions/address`表示从根节点开始的路径
  • **关键字:$id**
可以在上面的定义中加入id属性,这样可以通过id属性,这样可以通过id属性的值对该schema进行引用,而不需要完整的路径。

"address": {
    "type": "object",
    "$id" : "address",
    "properties": {
        "street_address": { "type": "string" },
        "city":           { "type": "string" },
        "state":          { "type": "string" }
    },
    "required": ["street_address", "city", "state"]
}
  • **关键字:$ref**
关键字`$ref`可以用在任何需要使用json schema的地方。如上例中,billing_address的value应该是一个json schema,通过一个`$ref`替代了。

`$ref`的value,是该schema的定义在json中的路径,以#开头代表根节点。
{
    "properties": {
        "billing_address": { "$ref": "#/definitions/address" },
        "shipping_address": { "$ref": "#/definitions/address" }
    }
}
如果schema定义了$id属性,也可以通过该属性的值进行引用。

{
    "properties": {
        "billing_address": { "$ref": "#address" },
        "shipping_address": { "$ref": "#address" }
    }
}

10. 通用关键字

  • enum**关键字:enum**
可以在任何json schema中出现,其value是一个list,表示json数据的取值只能是list中的某个
{
    "type": "string",
    "enum": ["red", "amber", "green"]
}
上例的schema规定数据只能是一个string,且只能是"red""amber""green"之一。
  • metadata**关键字:title,description,default,example**
{
    "title" : "Match anything",
    "description" : "This is a schema that matches anything.",
    "default" : "Default value",
    "examples" : [
        "Anything",
        4035
    ]
}
只作为描述作用,不影响对数据的校验

基础学习

AJV网站

校验数据 AJV
JSON Schema keywords

1.校验数据

const schema = {
    type:'string',
    minLength:10
}
const data = 'ww'
const validate = ajv.compile(schema)
const valid = validate(data)
if (!valid) console.log(validate.errors)

[
  {
    keyword: 'minLength',//判断错误的字段  说明长度不够10
    dataPath: '',
    schemaPath: '#/minLength',
    params: { limit: 10 },
    message: 'should NOT be shorter than 10 characters'
  }
]

2.关键字 Formats (只针对string和number类型,其他不起作用)

1.在AJV网站中搜索Formats 相关介绍

3.自定义format

const Ajv = require('ajv')
const ajv = new Ajv() // options can be passed, e.g. {allErrors: true}
const schema = {
    type:'object',
    properties:{
        name:{
            type:'string',
            format:'test'//格式为自定义的test
        },
}
//addFormat是ajv提供的 并非是Json Schema提供
ajv.addFormat('test',(data)=>{
    console.log(data,'-----')
    return data === 'haha' //格式必须为haha
})
const data = {
    name:'haha'
}
const validate = ajv.compile(schema)
const valid = validate(data)
if (!valid) console.log(validate.errors)

4.自定义关键字

const Ajv = require('ajv')
const ajv = new Ajv() // options can be passed, e.g. {allErrors: true}
const schema = {
    type:'object',
    properties:{
        name:{
            type:'string',
            test:false
            // minLength:10
        },
        age:{
            type:'number'
        },
        pets:{
            type:'array',
            items:{
                type:'string'
            }
        },
        isWorker:{
            type:'boolean'
        }
    },
    required:['name','age']
}
//第一种  addKeyword方式
// ajv.addKeyword('test',{
//     validate(schema,data){
//         console.log(schema,data);//true haha
//         if(schema){
//             return true
//         }else {
//             return schema.length === 6
//         }
//     }
// })
//第二种  compile  
// ajv.addKeyword('test',{
//     compile(sch,parentSchema){
//         console.log(sch,parentSchema);//false { type: 'string', test: false }
//         return ()=> true//要return一个函数
//     },
//     metaSchema:{//校验test值必须为boolean
//         type:'boolean'
//     }
// })
//第三种 推荐用
ajv.addKeyword('test',{
    macro(){
        return {
            minLength:10//长度为10 
        }
    }
})
const data = {
    name:'haha',
    age:10,
    pets:["mimi","mama"],
    isWorker:true
}
const validate = ajv.compile(schema)
const valid = validate(data)
if (!valid) console.log(validate.errors)

5.如何改变错误信息的提示语言 ajv-i18n

(1)安装 yarn add ajv-i18n -Snpm install ajv-i18n
(2)在AJV官网搜索Usage 选择含有ajv-i18n 有相关用法

const Ajv = require("ajv") // version >= 8.0.0
const localize = require("ajv-i18n")  //引入
// or for JSON Type Definition
// const localize = require("ajv-i18n/localize/jtd")

const ajv = Ajv({allErrors: true, messages: false})
const validate = ajv.compile(schema)
const valid = validate(data)

if (!valid) {
  // ru for Russian
  //localize.ru(validate.errors)//第一种调用
  localize.zh(validate.errors)//第二种调用
  // string with all errors and data paths
  console.log(ajv.errorsText(validate.errors, {separator: '\n'}))
}

6.定义json schema关键字校验不通过 的错误信息定制 ajv-errors

npm install ajv-errors

const Ajv = require('ajv')
const ajv = new Ajv({allErrors: true,jsonPointers:true})//注意这里的写法
require("ajv-errors")(ajv /*, {singleError: true} */)
const schema = {
    type:'object',
    properties:{
        name:{
            type:'string',
            //test:false,
            minLength:10,
            errorMessage:{//针对不同错误作不同提示
                type:'必须是字符串',
                minLength:'长度不能小于10'
            }
        }
    }
}
//第三种 推荐用
ajv.addKeyword('test',{
    macro(){
        return {
            minLength:10
        }
    }
})
const data = {
    name:11
}
const validate = ajv.compile(schema)
const valid = validate(data)
if (!valid) {
    console.log(validate.errors)
}