JSON Schema

[TOC]

前言:什么是JSON Schema

JSON Schema,也称为JSON模式JSON Schema是描述你的JSON数据格式;

主要有以下作用:

  1. 对现有的JSON数据格式进行描述(字段类型、内容长度、是否必须存在、取值示例等);
  2. 是一个描述清晰、人机可读的文档;
  3. 自动测试、验证客户端提交的数据;

简单示例

把需要被验证的JSON文档称为instance,用来校验它的文档就是schema

需要被校验的instance

{
  "productId": 1,
  "productName": "A green door",
  "price": 12.50,
  "tags": [ "home", "green" ]
}

用来校验它的schema

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://example.com/product.schema.json",
  "title": "Product",
  "description": "A product from Acme's catalog",
  "type": "object",
  "properties": {
    "productId": {
      "description": "The unique identifier for a product",
      "type": "integer"  // productId为整形类型
    },
    "productName": {
      "description": "Name of the product",
      "type": "string"  // productName为字符串类型
    },
    "price": {
      "description": "The price of the product",
      "type": "number",    // price的值为任意数字类型
      "exclusiveMinimum": 0 // price的值应该大于0
    },
    "tags": {
      "description": "Tags for the product",
      "type": "array",  // TAG为数组类型
      "items": {
        "type": "string" // TAG的值为字符串类型
      },
      "minItems": 1,    // 至少包含一个TAG
      "uniqueItems": true  // TAG不重复
    }
  },
  "required": [ "productId", "productName", "price" ] // 必需字段:productId, productName, price,意味着tag可以忽略
}

1. JSON Schema 关键字

关键字 描述
$schema 指定 Json Schema版本,不同版本间不完全兼容,可省略
title 用于进行简单的描述,可以省略
description 用于进行详细的描述信息,可以省略
type 用于约束 json 的字段类型,其值可以是:null、string、number、integer、object、array、boolean
properties 定义属性字段,定义每个字段的键和值类型

2. JSON Schema类型

任意对象

对于任意对象实例都可以使用type,enumconst关键字。

关键字 描述
type 用于约束该实例的类型,其值可以是:null、string、number、integer、object、array、boolean
emun 该值应该是一个去重的数组,且至少包含一个元素(元素可以是任意类型包含null),代表该字段取值应该属于该数组中
const 该值可以是任意类型甚至null,等效于enum且只有一个元素的情况

object

对象类型主要有三个关键字:type限定类型,properties定义对象中的各个字段,required限定必需字段。

关键字 描述
properties 定义属性字段,定义每个字段的键和值类型
type 用于约束 json 的字段类型,其值可以是:null、string、number、integer、object、array、boolean
required(dependentRequired) 必需属性,数组类型,指定上述 properties 中哪些是必需的字段
maxProperties 最大属性个数
minProperties 最小属性个数
additionalProperties 将用于验证与 properties 不匹配的属性是否被允许
patternProperties 将正则表达式映射到模式。如果属性名称与给定的正则表达式匹配,则属性值必须符合定义的类型

举例:

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "http://example.com/root.json",
  "title": "The Root Schema",
  "description":"test",
  "type": "object",
  "required": [
    "foo",
    "bar",
],
  "properties": {
    "foo": {
      "type": "number"
    },
    "bar": {
      "const": "Must equal this value"
    }
  }
}

更多查阅:Object

Array

列表验证对于任意长度的数组非常有用,其中每个项都匹配相同的模式。

关键字 描述
items 值被定义成一个独立的校验对象,可以包含对应的字段属性。例如:type,如果是object还能添加properties
maxItems 数组最大的元素个数
minItems 数组最小的元素个数
uniqueItems type 为 array 时,数组元素是否唯一,如果唯一设置为true
Contains 包含模式,对数组中的一个或多个项进行验证
minContains / maxContains 可以与contains一起使用,以进一步指定模式匹配包含约束的次数。这些关键字可以是任何非负数,包括零。

举例:

{
    "$schema": "http://json-schema.org/draft-04/schema#",
    "title": "Product",
    "description": "A product from Acme's catalog",
    "type": "array",
    "items": {
        "type": "string"
    },
    "minItems": 1,
    "uniqueItems": true
}

使用prefixItems数组还可以设置多个校验,用于数字值类型不固定的情况:

如果我们有这样一条数组数据:

1600 Pennsylvania Avenue NW

他们的类型为:

// number: 地址号,数字
// street_name: 街道名,字符串
// street_type: 街道类型,多个字符串中的一个也就是枚举
// direction:地址的城市方向,也是枚举。
[number, street_name, street_type, direction]

例子的schema为:

{
  "type": "array",
  "prefixItems": [
    { "type": "number" },
    { "type": "string" },
    { "enum": ["Street", "Avenue", "Boulevard"] },
    { "enum": ["NW", "NE", "SW", "SE"] }
  ]
}

但这个schema只能校验前4个值,如果我们的数组有了更多的值时:

[1600, "Pennsylvania", "Avenue", "NW", "Washington"]

此时schema应调整为:

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

更多查阅:Array

String

关键字 描述
maxLength 数组最大的元素个数
minLength 数组最小的元素个数
format 允许对常用的某些类型的字符串值进行基本的语义标识,format只是一个注释,并不影响验证。
pattern 用于将字符串限制为特定的正则表达式。

举例:

{
    "$schema": "http://json-schema.org/draft-04/schema#",
    "title": "Product",
    "description": "A product from Acme's catalog",
    "type": "object",
    "properties": {
        "ip": {
            "type": "string",
            "pattern": "w+([-+.]w+)*@w+([-.]w+)*.w+([-.]w+)*"
        },
        "host": {
            "type": "phoneNumber",
            "pattern": "((d{3,4})|d{3,4}-)?d{7,8}(-d{3})*"
        },
    },
    "required": [
        "ip",
        "host"
    ]
}

Enum

枚举类型,允许校验对象和数组2种写法:

// 方式一,对象写法
{
    "type": "object",
    "properties": { 
      "street_type": {
            "type": "string",
            "enum": [
                "Street",
                "Avenue",
                "Boulevard"
            ]
        }
    }
}
// 方式二,数组
{
    "type": "object",
    "properties": { 
      "street_type": [
                "Street",
                "Avenue",
                "Boulevard"
            ]
    }
}

Numeric

关键字 描述
multipleOf 是某数的倍数,必须大于0的整数
minimum 对于数值x: xminimum
exclusiveMinimum 对于数值x: x > exclusiveMinimum
maximum 对于数值x: xmaximum
exclusiveMaximum 对于数值x: x < exclusiveMaximum
Integer

integer类型用于整型数。JSON对整数和浮点值没有不同的类型。因此,小数点的存在或不存在不足以区分整数和非整数。例如:1和1.0是在JSON中表示相同值的两种方法,JSON模式认为该值是整数。

Number

number 关键字可以描述任意长度,任意小数点的数字。

Boolean

布尔类型只匹配两个特殊值:true和false。

注意:不接受非true或false的值,例如1和0。

3. 模式构成

allOf

给定的数据必须对所有给定的子模式有效(AND操作)。

{
  "allOf": [
    { "type": "string" },
    { "maxLength": 5 }
  ]
}
// "short"    ==> ok
// "too long" ==> failed
anyOf

给定的数据必须对给定的任何(一个或多个)子模式有效(OR操作)。

{
  "anyOf": [
    { "type": "string", "maxLength": 5 },
    { "type": "number", "minimum": 0 }
  ]
}
// "short"    ==> ok
// "too long" ==> failed
// 12         ==> ok
// -1         ==> failed

oneOf

给定的数据必须对给定的一个子模式有效(XOR操作)。

{
  "oneOf": [
    { "type": "number", "multipleOf": 5 },
    { "type": "number", "multipleOf": 3 }
  ]
}
// 10    ==> ok
// 2     ==> failed
// 7     ==> ok
// 15    ==> failed

4. Json schema 常用的在线工具

5. gojsonschema

package main

import (
    "fmt"
    "github.com/xeipuuv/gojsonschema"
)

func main() {

    schemaLoader := gojsonschema.NewReferenceLoader("file://./schema.json") // 从文件加载
    documentLoader := gojsonschema.NewStringLoader(`{"a":"b"}`) // 待校验的json数据

    result, err := gojsonschema.Validate(schemaLoader, documentLoader)
    if err != nil {
        panic(err.Error())
    }

    if result.Valid() {
        fmt.Printf("The document is valid\n")
    } else {
        fmt.Printf("The document is not valid. see errors :\n")
        for _, desc := range result.Errors() {
            fmt.Printf("- %s\n", desc)
        }
    }
}

参考资料

《Getting Started Step-By-Step》

《Understanding JSON Schema》

《JSON Schema Validation: A Vocabulary for Structural Validation of JSON》

《JSONSchema基础知识》

《golang校验json数据内容》

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,718评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,683评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,207评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,755评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,862评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,050评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,136评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,882评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,330评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,651评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,789评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,477评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,135评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,864评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,099评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,598评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,697评论 2 351

推荐阅读更多精彩内容