• json schema实际运用


    一、背景

    根据实际需求,发现前后端都需要对数据进行校验,且校验字段较多。为避免在代码中加入繁琐的判断,且能前后端保持统一标准,我们使用JSON schema规范来进行数据的校验。

    二、JSON Schema

    2.1 定义

    json-schema 中文意思是 JSON的模式。定义一个JSON的规范,对数据的格式进行约束。比如数据的类型,数字number,字符串 string, 对象Object;数字的大小和区间范围;是否要求必填等等;

    2.2 例子

    2.2.1 要求

    如下是一个校验字段fieldA,fieldB,fieldC的样例。我们要求fieldA是字符串,且值为testString;fieldB是数字类型,大于0(不等于0);三个字段为必填字段。

    {
      "type": "object",
      "properties": {
        "fieldA": {
          "type": "string",
          "enum": [
            "testString"
          ]
        },
        "fieldB": {
          "type": "number",
          "exclusiveMinimum": 0
        }
      },
      "required": [
        "fieldA",
        "fieldB",
        "fieldC"
      ]
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    2.2.2 待验证数据

    {
      "fieldA": "test",
      "fieldB": 0
    }
    
    • 1
    • 2
    • 3
    • 4

    2.2.3 验证

    在线验证地址 https://jsonschemalint.com/#!/version/draft-07/markup/json

    2.3 版本

    json schema 存在多个版本,当前最新版本为draft-07。 不同版本之间规范也不同。部分实现json schema框架默认draft4,可能大家使用过程中会造成麻烦。为了统一规范,可以在后续实现中,显式设置版本号。当然也可是每条检验规范显示设置一个版本号,不过那样太麻烦了。

    版本exclusiveMaximumexclusiveMaximum
    draft-04取值为 true false,与minimum结合使用,举例 a>0,则exclusiveMaximum: true, minimum:0取值为 true false,与maximum结合使用,举例 a<0,则exclusiveMaximum: true, maximum:0
    draft-07数字,举例 a>0,则exclusiveMinimum : 0数字,举例 a<0,则exclusiveMaximum: 0

    三、实现

    第二部分只是定义了一个规范,现在讲述一下如何实现。虽然JSON schema是脱离语言的,但是实现还是跟语言相关的。

    3.1 前端

    框架地址介绍是否选用
    ajvhttps://github.com/ajv-validator/ajvdraft-07, -06, -04 for Node.js and browsers - supports custom keywords and $data reference (MIT)选用
    djvhttps://github.com/korzio/djvdraft-06, -04 for Node.js and browsers (MIT)
    Hyperjump JSVhttps://github.com/jdesrosiers/json-schema2019-09, draft-07, -06, -04 Built for Node.js and browsers. Includes support for custom vocabularies. (MIT)
    vue-vuelidate-jsonschemahttps://github.com/mokkabonna/vue-vuelidate-jsonschemadraft-06 (MIT)
    @cfworker/json-schema@cfworker/json-schema2019-09, draft-07, -06, -04 Built for Cloudflare workers, browsers, and Node.js (MIT)

    选用ajv。ajv 是最优的选择。
    ajv 是对 JSON Schema 支持最全的一个库;
    性能在现有的库中也很优越, 排在第二位,;
    排第一位 djv 没有实现 JSON Schema 的最新特性, 我们需要使用最新版规范draft-07;
    ajv 与 djv 性能上很接近, 且 ajv比djv的性能更优越;
    ajv的社区比djv活跃;

    3.1.1 引入

    npm install ajv
    
    • 1

    3.1.2 demo代码

    // 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: {
       r93#:{ type:"string", enum:["a"]},
       r94#:{ type:"string", enum:["b"]},
        r156#:{anyOf:[{type:"number",exclusiveMinimum:0},{type:"number",exclusiveMaximum:0}]},
      },
      required: ["r93#","r94#",,"r156#"],
      additionalProperties: false,
    }
    // 待校验的数据
    const data = {
      r156#: 0,
       r93#: "1",
       r94#: "b",
    }
    
    const validate = ajv.compile(schema)
    const valid = validate(data)
    if (!valid) console.log(validate.errors)
    
    • 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

    3.2 后端

    框架地址介绍是否选用
    networknt/json-schema-validatorhttps://github.com/networknt/json-schema-validatordraft-07, -06, -04 Support OpenAPI 3.0 with Jackson parser (Apache License 2.0)选用
    everit-org/json-schemaeverit-org/json-schemadraft-07, -06, -04 (Apache License 2.0)
    snowy-jsonhttps://github.com/ssilverman/snowy-json2019-09, draft-07, -06 Uses Maven for the project and Gson under the hood. (GNU Affero General Public License v3.0)

    后端选用networknt框架。根据框架比较,

    1.networknt是耗时最少的;

    2.支持jackson;

    3.最小侵入,只引入一个jar包,不会造成冲突;

    3.支持高版本jdk,jdk1.8以上,;

    4.社区比较活跃。

    3.2.1 引入

    <dependency>
        <groupId>com.networkntgroupId>
        <artifactId>json-schema-validatorartifactId>
        <version>1.0.72version>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    3.2.2 demo代码

    import com.fasterxml.jackson.databind.JsonNode;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.networknt.schema.JsonSchema;
    import com.networknt.schema.JsonSchemaFactory;
    import com.networknt.schema.SpecVersion;
    import org.apache.commons.collections.CollectionUtils;
    import org.apache.commons.lang3.StringUtils;
    
    import java.util.HashMap;
    import java.util.Map;
    
    public class JsonSchemaUtil {
    
        public static boolean getPatternIdByRule(String checkExpression, JsonNode json) {
            if (StringUtils.isBlank(checkExpression)) {
                return false;
            }
            try {
                ObjectMapper mapper = new ObjectMapper();
                JsonNode rootNode = mapper.readTree(checkExpression);
                // 需要明确指定版本为draft7
                JsonSchemaFactory validatorFactory = JsonSchemaFactory
                        .builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7))
                        .objectMapper(mapper)
                        .build();
                JsonSchema newSchema = validatorFactory.getSchema(rootNode);
                if (CollectionUtils.isNotEmpty(newSchema.validate(json))) {
                    return false;
                } else {
                    return true;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            return false;
        }
    
        public static void main(String[] args) {
            ObjectMapper newMapper = new ObjectMapper();
            Map<String, Object> ruleMap = new HashMap<>();
            ruleMap.put("r156#", 0);
            ruleMap.put("r93#", "1");
            ruleMap.put("r94#", "b");
            JsonNode json = newMapper.valueToTree(ruleMap);
            System.out.println("空对象 校验成功 " + getPatternIdByRule("{}", json));
            System.out.println("r156#  校验失败 " + getPatternIdByRule("{ \"type\":\"object\", \"properties\":{ \"r156#\":{\"anyOf\":[{\"type\":\"number\",\"exclusiveMinimum\":0},{\"type\":\"number\",\"exclusiveMaximum\":0}]}}, \"required\":[\"r156#\"] }", json));
            System.out.println("r93#   校验失败 " + getPatternIdByRule("{ \"type\":\"object\", \"properties\":{ \"r93#\":{ \"type\":\"string\", \"enum\":[\"a\"] } },\"required\": [\"r93#\"] } "
                    , json));
            System.out.println("r94#   校验成功 " + getPatternIdByRule("{ \"type\":\"object\", \"properties\":{ \"r94#\":{ \"type\":\"string\", \"enum\":[\"b\"] } },\"required\": [\"r94#\"] } "
                    , json));
    
        }
    
    }
    
    • 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
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    #四、坑

    有些坑是要避开的

    1. 要显示设置json schema的版本?

    不同版本的json schema规范是不一样的,可详见 2.3 版本的 介绍,当前最新版本是draft07。举例,后端networknt框架 可调用builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7))指定

    1. ajv 和 networknt 是目前最好的选择吗?

    通过比较,ajv 和 networknt 是目前最好的选择。当然大家可以根据实际情况选择

    3.不同框架使用的JSONjar一样吗?

    不一样。比如 后端networknt框架使用jackson, 后端everit框架使用org.json

    参考资料

    json schema介绍 https://blog.csdn.net/liuxiao723846/article/details/108523139

    json schema介绍 https://cloud.tencent.com/developer/article/1617902

    json schema介绍 https://www.shouxicto.com/article/2783.html

    在线编辑和校验 https://jsonschemalint.com/#!/version/draft-07/markup/json

  • 相关阅读:
    CAS(Compare and swap)比较并交换算法解析
    数据库常见面试题
    痞子衡嵌入式:i.MXRT中FlexSPI外设不常用的读选通采样时钟源 - loopbackFromSckPad
    互联网Java工程师面试题·Java 并发编程篇·第五弹
    promise.race方式使用
    [Games 101] Lecture 13-16 Ray Tracing
    基于SSM+Jsp+Mysql的多人命题系统
    [C++]11版本新特性1:initialize list、auto、decltype
    LeetCode48旋转图像
    边缘计算物联网网关在机械加工行业的应用及作用分享
  • 原文地址:https://blog.csdn.net/sdtvyyb_007/article/details/126107314