blog
blog copied to clipboard
JSON 与 JSON Schema:从结构到规范的转换之旅
什么是 JSON Schema?
JSON Schema 是对 JSON 数据格式的描述和规范,提供了一种方式来约束和明确数据的类型和结构。可以把 JSON Schema 看作是 JSON 格式的一种“类型系统”,它就像 TypeScript 对 JavaScript 的作用一样。
数据类型
在 JSON Schema 中使用最多的是type
关键字,它包含了 JSON 格式的基本类型
类型 | 描述 |
---|---|
string | 字符串型,双引号包裹的 Unicode 字符和反斜杠转义字符 |
number | 数字型,包括整型(int)和浮点数型(float) |
boolean | 布尔型,true 或 false |
object | 对象型,无序的键:值对集合 |
array | 数组型,有序的值序列 |
null | 空型 |
关键字
下面以 Object 类型为例,列举在 JSON Schema 中经常使用到的关键字和作用。示例 JSON Schema 如下:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"$id": "https://example.com/schemas/person",
"title": "base info",
"description": "base information about person",
"type": "object",
"required": ["name", "age", "phone"],
"definitions": {
"name": {
"type": "string",
"minLength": 1,
"maxLength": 10
}
},
"properties": {
"name": {
"type": "string",
"minLength": 1,
"maxLength": 10
},
"age": {
"type": "number",
"minimum": 18,
"exclusiveMinimum": true,
"maximum": 65,
"exclusiveMaximum": true
},
"phone": {
"type": "string",
"pattern": "^1\\d{10}$"
},
"parents": {
"type": "array",
"items": [{ "$ref": "#/definitions/name" }],
"minItems": 1,
"maxItems": 2,
"uniqueItems": true
},
"address": {
"type": "object",
"properties": {
"city": {
"type": "string",
"enum": ["guangzhou", "beijing"]
}
}
}
}
}
这个示例 JSON Schema 描述了一个人的基本信息对象,包含了多个字段和相应的验证规则。
关键字 | 描述 |
---|---|
$schema | 声明此 json 片段属于 JSON Schema,并遵循所声明的 JSON Schema 版本规范 |
$id | 为 JSON Schema 声明一个统一资源标识符,使解析$ref 时能够引用片段 |
title | 为 JSON Schema 文件提供标题 |
description | 为 JSON Schema 文件提供描述信息 |
definitions | 声明子 schema,使解析$ref 能够引用片段 |
$ref | 引用 JSON Schema 片段 |
required | 定义对象类型 properties 所声明的字段是否必须,值必须是数组,数组中的元素必须是字符串类型且唯一 |
type | 定义元素的类型 |
properties | 定义对象类型里的属性(键值对),每个字段的值都是一个有效的 schema 片段,用来限制每个字段的格式 |
minimum | 约束取值范围,标识取值范围应该大于或等于 minimum |
exclusiveMinimum | 假若 minimum 或 exclusiveMinimum 同时存在,且 exclusiveMinimum 为 true,则取值范围大于 minimum |
maximum | 约束取值范围,标识取值范围应该小于或等于 maximum |
exclusiveMaximum | 假若 maximum 或 exclusiveMaximum 同时存在,且 exclusiveMaximum 为 true,则取值范围小于 maximum |
minLength | 字符串类型数据的最小长度 |
maxLength | 字符串类型数据的最大长度 |
pattern | 使用正则表达式约束字符串类型数据 |
items | 用来定义数组类型的子元素,值必须为数组,且是一个有效的 schema 片段 |
minItems | 定义数组类型大小的最小长度 |
maxItems | 定义数组类型大小的最大长度 |
uniqueItems | 定义数组类型子元素是否必须唯一 |
enum | 用来限制值的范围,值必须在 enum 所指定的集合里面 |
以上列举的是一些常用到的关键字。要深入了解和使用更多关键字,可以参考 JSON Schema 官方文档。
JSON Schema 的应用场景
JSON Schema 的优点在于可以对数据类型进行描述,方便理解。同时也让机器“读懂”,比如数据校验或、输入检测提示、自动化测试等等。
- 数据校验
基于 JSON Schema 提供多种校验约束条件,可以定义数据的校验规则,通过 JSON Schema 进行数据校验,多用于接口请求参数校验,表单校验,和数据校验自动化测试上
验证器
JSON Schema 校验工具:Ajv
// or ESM/TypeScript import
import Ajv from 'ajv'
// Node.js require:
const Ajv = require('ajv')
const ajv = new Ajv() // options can be passed, e.g. {allErrors: true}
const schema = {
type: 'object',
properties: {
foo: { type: 'integer' },
bar: { type: 'string' }
},
required: ['foo'],
additionalProperties: false
}
const data = {
foo: 1,
bar: 'abc'
}
// 最佳性能:compile 和 getSchema 方法,使用由 compile 或 getSchema 方法返回的编译函数时可以获得最佳性能。
const validate = ajv.compile(schema)
// 每次调用验证函数(或 ajv.validate )时, errors属性都会被覆盖
const valid = validate(data)
if (!valid) console.log(validate.errors)
- 提供更为准确可靠的 mock 数据
基于 JSON Schema 提供多种校验约束条件,可以使用它原生的能力来生成更为准确可靠的 mock 数据
使用 JSON Schema 生成 mock 数据在线工具:
- 基于 JSON Schema 配置文件渲染 UI 组件
例如: https://github.com/lljj-x/vue-json-schema-form/tree/master, 其基于 Vue/Vue3,Json Schema 和 ElementUi/antd/iview3/naiveUi 等生成 HTML Form 表单,可用于活动编辑器、h5 编辑器、cms 等数据配置;支持可视化生成表单 Schema 。
Schema
{
"title": "测试注册表单",
"description": "A simple form example.",
"type": "object",
"required": ["firstName", "lastName"],
"ui:order": ["lastName", "firstName", "*", "password"],
"properties": {
"firstName": {
"type": "string",
"title": "First name",
"default": "Jun"
},
"lastName": {
"type": "string",
"title": "Last name",
"ui:options": {
"description": "请输入你的姓"
},
"err:required": "必须输入Last Name"
},
"price": {
"type": "string",
"description": "最多输入两位小数点,最大值 999999.99",
"title": "价格",
"format": "price"
},
"age": {
"type": "integer",
"title": "Age",
"maximum": 80,
"minimum": 16
},
"bio": {
"type": "string",
"title": "Bio",
"minLength": 10
},
"password": {
"type": "string",
"title": "Password",
"minLength": 3
},
"telephone": {
"type": "string",
"title": "Telephone",
"minLength": 10
}
}
}
FormData
{
"firstName": "Jun",
"lastName": "Liu",
"age": 80,
"bio": "知道的越多、就知道的越少",
"password": "My.Pass",
"telephone": "1881446xxxx"
}
- 低代码 low-code
- 整个页面规范 JSON Schema
- 组件配置页按 JSON Schema 驱动
- 视图页消费解析 JSON Schema 并渲染视图
低代码组件配置示例
1. 对象
(1)方案 A
const props = {
// data 组件内置
scrollArea: {
type: Object,
desc: '滚动条区域',
def: () => {
return {
scrollType: {
type: String,
desc: '滚动条展示文案类型'
},
scrollTypeSwitch: {
type: Boolean,
desc: '展示开关'
}
}
}
},
btnStyle: {
type: Object,
desc: '按钮自定义样式',
def: () => {}
},
btnWrapStyle: {
type: Object,
desc: '按钮区域自定义样式',
def: () => {}
},
clickArea: {
type: 'CLICK_AREA',
desc: '配置图片可点击区域'
}
}
(2)方案 B
const props = {
data: {
type: Object,
desc: '数据源配置',
default: () => {
return {
headImageUrl: {
type: String,
desc: '头图链接',
component: 'Upload',
default:
'https://ecuat.tk.cn/tkcms/file/upload/mob/productImg/zwt1.png'
},
saleImageUrl: {
type: String,
desc: '卖点图链接',
component: 'Upload',
default:
'https://ecuat.tk.cn/tkcms/file/upload/mob/productImg/zwt2.png'
}
}
}
},
scrollArea: {
type: Object,
desc: '滚动条区域',
default: () => {
return {
scrollType: {
type: String,
desc: '滚动条展示文案类型',
default: 'normal',
component: 'Select',
selectList: [
{
key: 'normal',
value: '默认'
},
{
key: 'womon',
value: '女性'
},
{
key: 'child',
value: '少儿'
},
{
key: 'pet',
value: '宠物'
}
]
},
scrollTypeSwitch: {
type: Boolean,
desc: '展示开关',
default: true
}
}
}
},
btnStyle: {
type: Array,
component: 'Css',
desc: '投保按钮自定义样式',
default: () => [
{
background: 'linear-gradient(180deg,#63c3ff, #3f80ff 76%)'
},
{
boxShadow:
'0px 1px 4px 0px rgba(87,163,253,0.50), inset 1px 0px 6px 0px rgba(132,186,255,0.40)'
}
]
},
btnWrapStyle: {
type: Object,
desc: '按钮区域自定义样式',
default: () => {
return {
top: {
type: String,
desc: '按钮距离顶部的距离',
default: '2.76rem'
}
}
}
}
}
(3) 方案 C
{
"title": "组件的配置",
"description": "组件的描述",
"type": "object",
"properties": {
"data": {
"title": "数据源配置",
"type": "object",
"properties": {
"headImageUrl": {
"type": "string",
"description": "头图链接"
},
"saleImageUrl": {
"type": "string",
"description": "卖点图"
}
},
"required": ["headImageUrl", "saleImageUrl"]
},
"scrollArea": {
"title": "滚动条区域",
"type": "object",
"properties": {
"scrollType": {
"type": "string",
"description": "滚动条展示文案类型",
"oneOf": [
{ "const": "normal", "title": "普通" },
{ "const": "womon", "title": "女性" },
{ "const": "child", "title": "少儿" },
{ "const": "pet", "title": "宠物" }
]
},
"scrollTypeSwitch": {
"description": "展示开关",
"type": "boolean"
}
}
},
"btnStyle": {
"title": "按钮样式",
"type": "array",
"items": {
"type": "object"
},
"minItems": 0,
"uniqueItems": true
},
"btnWrapStyle": {
"title": "按钮外部样式",
"type": "object",
"properties": {
"top": {
"type": "integer",
"description": "按钮距离顶部的距离",
"minimum": 0,
"maximum": 10
}
}
}
}
}
默认值采用,新增
default
字段
定制组件采用,新增
useComponent
字段 b
2. 数组
(1) 方案 A
const props = {
gapStyle: { type: 'Style', desc: '间距样式' },
config: { type: 'LIST_RENDER' }
}
(2) 方案 B
const props = {
title: {
type: String,
desc: '主标题',
default: '图片区'
},
productList: {
type: Array,
component: 'Uploadevent',
desc: '图片列表',
default: () => [
{
url: '',
event: []
}
]
}
}
(3) 方案 C
{
"title": "组件的配置",
"description": "组件的描述",
"type": "object",
"properties": {
"title": {
"description": "标题",
"type": "string",
"maxLength": 10
},
"productList": {
"type": "array",
"items": {
"url": {
"description": "链接地址",
"type": "string"
},
"events": {
"type": "array",
"description": "事件配置",
"items": {
"type": { "type": "string" },
"title": { "type": "string" },
"clickArea": {
"type": "array",
"items": {
"type": "string"
}
}
},
"minItems": 0
}
},
"required": ["url"],
"minItems": 0
}
}
}
3. 递归
使用 $ref
关键字引用自己的子 schema 片段,实现递归模式,可以用于树形结构的描述
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "$ref递归调用",
"definitions": {
"node": {
"type": "object",
"properties": {
"children": {
"type": "array",
"items": {
"$ref": "#/definitions/node"
}
}
}
}
},
"type": "object",
"properties": {
"tree": {
"$ref": "#/definitions/node"
}
}
}
以下的 JSON 结构才能在上述的 JSON Schema 校验通过:
{
"tree": {
"children": [
{
"children": [
{
"children": []
}
]
}
]
}
}
参考
- https://json-schema.org/
- https://json-schema.org/draft/2019-09/json-schema-validation#rfc.section.6.1.2
- https://json-schema.apifox.cn/
- npm
- https://github.com/ajv-validator/ajv
- https://github.com/tdegrunt/jsonschema
- vue 相关
- https://github.com/lljj-x/vue-json-schema-form/tree/master https://formilyjs.org/zh-CN