JSON,是一种轻量级的数据交换格式,基本结构是“名称/值”对的集合,非常易于人阅读和编写,也易于机器解析和生成。
之前的文章: cJSON基础介绍与代码测试,已介绍过C语言格式的cJSON库的使用,本篇来介绍与JSON紧密相关的JSON Schema。
1 问题引入
假设实际的项目中,我们使用json来描述一款产品的信息,该产品具有:
- 标识符:
productId
- 商品名称:
productName
- 销售价格:
price
- 一组可选的标签:
tags
用JSON表示,如下:
1 2 3 4 5 6
| { "productId": 1, "productName": "A green door", "price": 12.50, "tags": [ "home", "green" ] }
|
看起来很简洁明了,但这种描述也留有一些问题,比如:
productId
的含义是什么?
productName
是必须的吗?
price
可以为零吗?
tags
都是字符串吗?
假如对于这里的JSON数据,我们随意赋予些不符合实际的数值,则会造成意外的影响。因此,就需要有一种规则来约束JSON数据的有效性,这就要通过JSON Schema来描述了。
2 JSON Schema
JSON Schema是一个提议的 IETF 标准,用于解决前面提到的数据格式的描述问题。JSON Schema本身使用的也是JSON数据格式。
下面来看一下JSON Schema的基本语法。
2.1 Schema的开头
我们从四个关键字的属性开始
$schema
:用于指定JSON Schema版本信息,可以省略。注:该关键字若使用,其值必须使用官方提供的值,不能自己随便写。
$id
: 定义了Schema的 URI,以及Schema内其他 URI 引用解析所依据的基本URI,可以省略。
title
和description
: 仅是描述性的。它们不会对正在验证的数据添加约束,可以省略。
type
: 验证JSON数据的第一个约束。此例子中,我们的JSON数据必须是一个JSON对象。
下面就是一个基本的JSON Schema:
1 2 3 4 5 6 7
| { "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://example.com/product.schema.json", "title": "Product", "description": "A product in the catalog", "type": "object" }
|
从以上例子中,我们简单了解了JSON Schema开头的结构:
- Schema关键字:
$schema
和$id
- Schema注释:
title
和description
- 验证关键字:
type
2.2 属性定义
type
关键字描述了json数据的整体类型,那json数据内部元素如何描述呢,就要再借助properties
关键字了。
再介绍一个required
关键字,用来描述JSON数据中哪些关键字是必须的,例如productId
是标识产品的唯一数值,是必需的。
将属性信息加入后的JSON Schema如下:
1 2 3 4 5 6 7 8 9 10 11 12
| { "title": "Product", "description": "A product from Acme's catalog", "type": "object", "properties": { "productId": { "description": "The unique identifier for a product", "type": "integer" } }, "required": [ "productId" ] }
|
模仿对产品IDproductId
的描述,继续来补充产品名称productName
。
productName
也是必需的,它和productName
之间实际上没有任何区别,因为计算机通常关注ID,而人类通常关注名称。注意required
关键字是一个字符串数组,它可以记录多个值。
补充后的JSON Schema如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| { "title": "Product", "description": "A product from Acme's catalog", "type": "object", "properties": { "productId": { "description": "The unique identifier for a product", "type": "integer" }, "productName": { "description": "Name of the product", "type": "string" } }, "required": [ "productId", "productName" ] }
|
此时,根据JSON Schema的描述,我们的JSON数据是这样:
1 2 3 4
| { "productId": 1, "productName": "A green door" }
|
2.3 深入了解属性
假设产品不是免费的,即price
都是大于0,如何描述?
我们的JSON数据现在添加了price
数据:
1 2 3 4 5
| { "productId": 1, "productName": "A green door", "price": 12.50 }
|
我们可以用exclusiveMinimum
关键字来验证指定值的最小值。
将price
补充后的JSON Schema如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| { "title": "Product", "description": "A product from Acme's catalog", "type": "object", "properties": { "productId": { "description": "The unique identifier for a product", "type": "integer" }, "productName": { "description": "Name of the product", "type": "string" }, "price": { "description": "The price of the product", "type": "number", "exclusiveMinimum": 0 } }, "required": [ "productId", "productName", "price" ] }
|
接下来,再来看一下tags
关键字。假如这个产品有如下特性:
- 如果有标签,则必须至少有一个标签,
- 标签必须是文本。
- 标签必须是唯一的,产品内无重复。
- 标签不是必须要有的。
根据以上要求,有对应的Schema描述:
- 对
tags
的type
验证关键字为array
,用minItems
关键字来验证数组中最少的元素个数。
- 用
items
关键字来定义数组中出现的内容。本例中,type
验证关键字的值就是string
。
- 用
uniqueItems
关键字来验证数组中的元素是相对彼此是唯一的。
- 不把
tags
添加到required
验证关键字数组中,表示它是可选的。
将tag
添加后的JSON Schema如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| { "title": "Product", "description": "A product from Acme's catalog", "type": "object", "properties": { "productId": { "description": "The unique identifier for a product", "type": "integer" }, "productName": { "description": "Name of the product", "type": "string" }, "price": { "description": "The price of the product", "type": "number", "exclusiveMinimum": 0 }, "tags": { "description": "Tags for the product", "type": "array", "items": { "type": "string" }, "minItems": 1, "uniqueItems": true } }, "required": [ "productId", "productName", "price" ] }
|
此时,根据JSON Schema的描述,我们的JSON数据是这样:
1 2 3 4 5 6
| { "productId": 1, "productName": "A green door", "price": 12.50, "tags": [ "home", "green" ] }
|
2.4 嵌套的数据结构
到目前为止,我们的JSON数据只有一个级别。
那多级的嵌套JSON数据(JSON数据内部的元素又是一个JSON对象),如何用JSON Schema描述呢?比如对该产品添加尺寸信息dimensions
,它本身又是一个JSON对象:
1 2 3 4 5 6 7 8 9 10 11
| { "productId": 1, "productName": "A green door", "price": 12.50, "tags": [ "home", "green" ], "dimensions": { "length": 7.0, "width": 12.0, "height": 9.5 } }
|
对于嵌套的数据,则JSON Schema也继续进行对应的描述即可。
将dimensions
添加后的JSON Schema如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| { "title": "Product", "description": "A product from Acme's catalog", "type": "object", "properties": { "productId": { "description": "The unique identifier for a product", "type": "integer" }, "productName": { "description": "Name of the product", "type": "string" }, "price": { "description": "The price of the product", "type": "number", "exclusiveMinimum": 0 }, "tags": { "description": "Tags for the product", "type": "array", "items": { "type": "string" }, "minItems": 1, "uniqueItems": true }, "dimensions": { "type": "object", "properties": { "length": { "type": "number" }, "width": { "type": "number" }, "height": { "type": "number" } }, "required": [ "length", "width", "height" ] } }, "required": [ "productId", "productName", "price" ] }
|
注:为了简洁起见,例子中省略了对dimensions
的description
描述
3 使用Shema验证JSON格式
有许多在线的网站可以通过JSON Schema来验证JSON数据,比如这里: https://jsonschemalint.com/#/version/draft-07/markup/json
将本篇使用的JSON Schema复制到浏览器左边框中,JSON数据复制到右边框中,可以看到两个框都是绿色的,且右下角提示验证通过。
我们手动搞些错误出来,比如将JSON数据中的price
字段删掉,则整个边框变红,下面提示缺少price。
再比如,将tags数组中增加一个重复的green标签,则也会进行错误提示。这些都是JSON Schema所起的实际作用。
4 总结
本篇主要介绍了三点:
- JSON Schema与JSON的关系:JSON Schema是对JSON数据格式的一种描述。
- JSON Schema的基本用法,通过一些常用的验证关键字来了解JSON Schema的用法:
title
和description
: 仅是描述性的,可以省略。
type
: 验证JSON数据的类型
properties
:描述JSON内部数据的具体属性
required
:描述JSON数据中哪些关键字是必须的
exclusiveMinimum
:验证指定值的最小值
minItems
:验证数组中最少的元素个数
items
:用来定义数组中出现的内容
uniqueItems
:验证数组中的元素是相对彼此是唯一的。
- 使用JSON Schema来验证对应的JSON数据是否符合要求。