java校验json的格式是否符合要求
在日常开发过程中,会有这样的需求,校验某个json是否是我们想要的数据格式,如果每个层级去判断,基本不太可能实现,当然java有开源的工具,我们可以直接使用
JSON Schema
JSON Schema 是用于验证 JSON 数据结构的强大工具,Schema可以理解为模式或者规则。
Json Schema定义了一套词汇和规则,这套词汇和规则用来定义Json元数据,且元数据也是通过Json数据形式表达的。Json元数据定义了Json数据需要满足的规范,规范包括成员、结构、类型、约束等。
JSON Schema 就是json的格式描述、定义、模板,有了他就可以生成任何符合要求的json数据
json-schema-validator
在java中,对json数据格式的校验,使用 json-schema-validator
,具体实例如下:
1. 引入依赖
<dependency> <groupId>com.github.fgegroupId> <artifactId>json-schema-validatorartifactId> <version>2.2.6version> dependency> <dependency> <groupId>com.fasterxml.jackson.coregroupId> <artifactId>jackson-coreartifactId> dependency> <dependency> <groupId>com.fasterxml.jackson.coregroupId> <artifactId>jackson-databindartifactId> dependency>
jackson-core
和 jackson-databind
是必须要引入的,他们为 json-schema-validator
必须的
2. 编写schema
如果我们要校验的数据格式如下:
{ "data": [ { "sex": "男", "name": "王小明", "age": 18 }, { "sex": "女", "name": "王小红", "age": 17 } ], "type": "human" }
外面是type和data,里面是一个数组,数组属性包括sex、name、age
编写schema文件
{ "type": "object", "properties": { "type": { "type": "string" }, "data": { "type": "array", "items": { "type": "object", "properties": { "name": { "type": "string", "maxLength": 3 }, "sex": { "enum": [ "男", "女" ] }, "age": { "type": "number" } }, "required": [ "name", "sex", "age" ] } } }, "required": [ "type", "data" ] }
以上json描述了目标json的数据格式,外层必须字段type、data,里面限制了name的最大长度 maxLength
为3,sex 为枚举值,只可取 男、女两个字符串,age 为number类型。
3. 代码实现
package com.example.validator.service.impl; import cn.hutool.core.io.resource.ResourceUtil; import com.example.validator.service.ValidatorService; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.github.fge.jsonschema.core.exceptions.ProcessingException; import com.github.fge.jsonschema.core.report.ProcessingReport; import com.github.fge.jsonschema.main.JsonSchemaFactory; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import java.io.File; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.Objects; @Slf4j @Service public class ValidatorServiceImpl implements ValidatorService { @Override public Map validatorJsonUnchecked(String body) { Map map = new HashMap<>(); String filePath = "validator" + File.separator + "validator.json"; ObjectMapper objectMapper = new ObjectMapper(); try { JsonNode jsonNodeSchema = objectMapper.readTree(ResourceUtil.readUtf8Str(filePath)); JsonNode jsonNode = objectMapper.readTree(body); ProcessingReport processingReport = JsonSchemaFactory.byDefault().getValidator().validate(jsonNodeSchema, jsonNode, true); if (!processingReport.isSuccess()) { processingReport.forEach(processingMessage -> { JsonNode missing = processingMessage.asJson().get("missing"); String keyword = processingMessage.asJson().get("keyword").asText(); // 如果缺失字段 if (!Objects.isNull(missing)) { missing.forEach(miss -> { String text = miss.asText(); map.put(text, text + " 字段缺失"); }); // 如果字段超长 } else if ("maxLength".equals(keyword)) { String field = processingMessage.asJson().get("instance").get("pointer").asText(); String value = processingMessage.asJson().get("value").asText(); field = field.substring(field.lastIndexOf("/") + 1); map.put(field, value + " 字段长度过长"); // 如果不在枚举范围内 } else if ("enum".equals(keyword)) { String field = processingMessage.asJson().get("instance").get("pointer").asText(); String value = processingMessage.asJson().get("value").asText(); field = field.substring(field.lastIndexOf("/") + 1); map.put(field, field + "字段值错误," + value + "不在枚举范围内"); } else if ("type".equals(keyword)) { String field = processingMessage.asJson().get("instance").get("pointer").asText(); String found = processingMessage.asJson().get("found").asText(); String expected = processingMessage.asJson().get("expected").toString(); field = field.substring(field.lastIndexOf("/") + 1); map.put(field, field + " 类型错误,现有类型: " + found + ", 预期类型:" + expected); } }); } } catch (IOException | ProcessingException e) { log.error("校验json格式异常", e); } return map; } }
以上代码首先获取了 要校验的json的标准文件 validator.json
,然后调用 JsonSchemaFactory.byDefault().getValidator().validate(jsonNodeSchema, jsonNode, true)
方法对传进来的json 进行了校验,这里 true
的意思是深度检查,如果没有这个参数,校验json的时候遇到第一个错误,就直接返回了
接下来构建测试方法
public static void main(String[] args) { ValidatorService validatorService = new ValidatorServiceImpl(); Map<String, Object> body = new HashMap<>(); HashMap<String, Object> one = new HashMap<String, Object>() {{ put("name", "王小明"); put("sex", "男"); put("age", 18); }}; HashMap<String, Object> two = new HashMap<String, Object>() {{ put("name", "王小明1"); put("sex", "未知"); put("age", "18"); }}; body.put("type", "human"); body.put("data", Arrays.asList(one,two)); Map map = validatorService.validatorJsonUnchecked(JSONUtil.toJsonStr(body)); System.out.println(map); }
4. 执行结果
{sex=sex字段值错误,未知不在枚举范围内, name=王小明1 字段长度过长, age=age 类型错误,现有类型: string, 预期类型:["integer","number"]}
5. 整理总结
如果schema 编写的时候,对列表使用了中括号 []
,那么当校验的时候只会校验数组中的第一个,这是一个坑,如下
{ "type": "object", "properties": { "type": { "type": "string" }, "data": { "type": "array", "items": [ { "type": "object", "properties": { "name": { "type": "string", "maxLength": 3 }, "sex": { "enum": [ "男", "女" ] }, "age": { "type": "number" } }, "required": [ "name", "sex", "age" ] } ] } }, "required": [ "type", "data" ] }
如果是这样的话,只会校验 data 数组的第一条数据,其他的有错误也不会报错!!
JSON Schema 功能很强大,支持表达式,支持是否允许额外属性,支持逻辑组合等,如果想了解更新json校验的知识,请参考下面参考文档