参考 官方网站
对于数据对接系统来说,接口的数据入参校验尤为重要,使用javax.validation相关注解进行校验对于java对象的关联太大,数据结构变动后必须开发人员调整java对象才能满足校验。为了让数据校验和java对象解耦,研究后发现可以使用JSON Schema来实现,定义好相关的schema文件,这个文件可以放在任意可访问的地方,也不需要关心需要校验的数据是属于那个java对象。只要熟悉JSON和一些schema语法,schema文件甚至可以由非开发人员编写。
JSON Schema本身也是JSON,它表现的是数据,是描述数据结的声明性格式,并不是程序。所以,它在表现元素之间的关联关系上一定限制。
使用$schema来声明使用的JSON模式规范是哪个版本: draft-04
{
"$schema": "http://json-schema.org/draft-04/schema#"
}
在definitions中定义的规则可以重复使用
"#"表示JSON的根节点
{
"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" }
}
}
标题
详细的说明
数据类型,支持:array,object,string,Integer,number, null;
type是object时具有成员属性
properties中的必填属性都放在这里
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "array",
"items": {
"type": "object",
"properties": {
"attachmentCode": {
"type": "string"
},
"attachmentFileName": {
"type": "string"
},
"md5": {
"type": "string"
}
},
"required": ["md5"]
}
}
{
"type": "array",
"items": [
{
"type": "object",
"properties": {
"attachmentCode": {
"type": "string"
},
"attachmentFileName": {
"type": "string"
},
"fileType": {
"type": "string"
},
"dataTimestamp": {
"$ref": "#/must_time"
},
"md5": {
"type": "string"
}
}
},
{
"type": "string",
"minLength": 5
},
{
"type": "number",
"minimum": 10
},
{
"type": "string"
}
]
}
{
"type": "array",
"items": [
{
"type": "string",
"minLength": 5
},
{
"type": "number",
"minimum": 10
}
],
"additionalItems": {
"type": "string",
"minLength": 2
}
}
上面的JSON Schema的意思是,待校验JSON数组第一个元素是string类型,且可接受的最短长度为5个字符,第二个元素是number类型,且可接受的最小值为10,剩余的其他元素是string类型,且可接受的最短长度为2。
{
"type": "array",
"contains": {
"type": "number"
}
}
["life", "universe", "everything", 42] // OK,包含一个number元素
["life", "universe", "everything", "forty-two"] // not OK,不包含number元素
[1, 2, 3, 4, 5] // OK
规定最少、最多有几个属性成员。
规定object类型是否允许出现不在properties中规定的属性,只能取true/false。
只有待校验的值能够被该关键字的值整除,才算通过校验。
如果含有该关键字的JSON Schema如下:
{
"type": "integer",
"multipleOf": 2
}
那么,2、4、6都是可以通过校验的,但是,3、5、7都是无法通过校验的,
当然了,2.0、4.0也是无法通过校验的。
如果含有multipleOf关键字的JSON Schema如下:
{ "type": "number", "multipleOf": 2.0 }
那么,2、2.0、4、4.0都是可以通过校验的,但是,3、3.0都是无法通过校验的。
元素可以通过校验的最大值
该关键字通常和maximum一起使用,当该关键字的值为true时,表示待校验元素必须小于maximum指定的值;当该关键字的值为false时,表示待校验元素可以小于或者等于maximum指定的值。
元素可以通过的最小值
与exclusiveMaximum相反
format只能是以下值:
date-time(时间格式)、email(邮件格式)、hostname(网站地址格式)、
ipv4、ipv6、uri、uri-reference、uri-template、json-pointer。
规定某些成员的依赖成员,不能在依赖成员缺席的情况下单独出现,属于数据完整性方面的约束。
有"credit_card"属性,则"billing_address" 属性不能缺席:
{
"type": "object",
"dependencies": {
"credit_card": ["billing_address"]
}
}
该关键字的值是一个数组,该数组至少要有一个元素,且数组内的每一个元素都是唯一的。
如果待校验的JSON元素和数组中的某一个元素相同,则通过校验。否则,无法通过校验。
该数组中的元素值可以是任何值,包括null。
元素被限定为一个常量值
必须满足全部的检验才能通过
满足一个或多个校验可以通过
只能满足一个校验才能通过
不满足这个校验才能通过
关键字指定一个默认值
java对象生成schema工具
<dependency>
<groupId>com.github.reinert</groupId>
<artifactId>jjschema</artifactId>
<version>1.16</version>
</dependency>
实际应用场景中,重复引用的情况比较多,这里说明下引用的使用
{
"$schema": "http://json-schema.org/draft-04/schema#",
"description": "定义时间字段的匹配规则",
"must_time": {
"description": "必填时间字段规则",
"oneOf": [
{
"type": "string",
"pattern": "(\\d{4}-\\d{1,2}-\\d{1,2} \\d{1,2}:\\d{1,2}:\\d{1,2})|(\\d{4}-\\d{1,2}-\\d{1,2})$|^\\d{1,13}$"
},
{
"type": "number"
}
]
},
"not_time": {
"description": "非必填时间字段",
"oneOf": [
{
"type": "string",
"pattern": "(\\d{4}-\\d{1,2}-\\d{1,2} \\d{1,2}:\\d{1,2}:\\d{1,2})|(\\d{4}-\\d{1,2}-\\d{1,2})$|^\\d{1,13}$"
},
{
"type": "number"
},
{
"type": "null"
}
]
}
}
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "array",
"id": "file:/E:/Time.json", //定义引用
"items": {
"type": "object",
"properties": {
"attachmentCode": {
"type": "string"
},
"attachmentFileName": {
"type": "string"
},
"fileType": {
"type": "string"
},
"dataTimestamp": {
"$ref": "#/must_time" //直接使用引用中的规则,对于重复引用的书写较为友好
},
"md5": {
"type": "string"
}
}
}
}
{
"attachment": {
"$ref": "file:/E:/Attachment.json" //使用整个schema文件的校验规则
"$ref": "file:/E:/Attachment.json#/age" //使用文件根节点下的age的校验规则
"$ref": "file:///Attachment.json#/age" //linux环境使用文件根节点下的age的校验规则
}
}
因为需要使用校验的结果,所以使用了这个库,性能上会差一点,支持草案draft-04 draft-03
官方也提供了一些开源工具:
<dependency>
<groupId>com.github.java-json-tools</groupId>
<artifactId>json-schema-validator</artifactId>
<version>2.2.14</version>
</dependency>
代码实现
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.fzzn.water.check.core.factory.CheckBuildFactory;
import com.github.fge.jackson.JsonLoader;
import com.github.fge.jackson.JsonNodeReader;
import com.github.fge.jsonschema.core.report.ProcessingMessage;
import com.github.fge.jsonschema.core.report.ProcessingReport;
import com.github.fge.jsonschema.main.JsonSchemaFactory;
import java.io.FileReader;
import java.util.Iterator;
sonNode schemaJson = new JsonNodeReader().fromReader(new FileReader("schema文件路径"));
JsonNode dataNoe = JsonLoader.fromString("待校验的JSON字符串");
ProcessingReport processingMessages = JsonSchemaFactory.byDefault().getValidator()
.validate(schemaJson, dataNoe, true);//true: 全部校验完再返回
System.out.println(processingMessages.isSuccess());
Iterator<ProcessingMessage> iterator = processingMessages.iterator();
JSONObject jsonNode;
while (iterator.hasNext()) {
ProcessingMessage message = iterator.next();
jsonNode = JSONUtil.parseObj(message.asJson().toString());
System.out.println("------>" + jsonNode.toString());
System.out.println("======>" + message);
}
输出示例:
------>{"schema":{"pointer":"/items/properties/md5","loadingURI":"#"},"instance":{"pointer":"/0/md5"},"level":"error","expected":["string"],"message":"instance type (null) does not match any allowed primitive type (allowed: [\"string\"])","found":"null","domain":"validation","keyword":"type"}
======>error: instance type (null) does not match any allowed primitive type (allowed: ["string"])
level: "error"
schema: {"loadingURI":"#","pointer":"/items/properties/md5"}
instance: {"pointer":"/0/md5"}
domain: "validation"
keyword: "type"
found: "null"
expected: ["string"]
拿到JSON格式的校验的结果,就可以针对性的处理了。
因篇幅问题不能全部显示,请点此查看更多更全内容