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.fge</groupId>
      <artifactId>json-schema-validator</artifactId>
      <version>2.2.6</version>
    </dependency><dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-core</artifactId>
      <version>2.3.0</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-core</artifactId>
      <version>2.3.0</version>
    </dependency>

jackson-corejackson-core 是必需要引进的,他们为 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. 代码完成

public Map validatorJsonUnchecked(String body) {
    Map<String, String> 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校验的知识,请参阅下面参阅文档

参阅文档

www.cnblogs.com/terencezhou…

json-schema.apifox.cn/

www.nuomiphp.com/a/stackover…