zoukankan      html  css  js  c++  java
  • 利用JsonSchema校验json数据内容的合规性(转)

    原文地址:Json schema

    背景:

    复杂的AJAX应用程序可以与数百个不同的JSON服务进行交互,因此,引入对客户端验证的需求。

    在处理校验问题方面有着很多的工具,但是通常可以将它们归为以下几类:

        * 判断数据是否已被正确格式化
        * 手动检查形式有误的数据并尝试纠正
        * 手动检查形式有误的数据并将有误数据丢弃
        * 自动检查形式有误的数据

    在这里只讨论自动校验方面的可用工具包中的json schema,son schema项目首页:http://json-schema.org/

    JSON schema是一个帮助你定义、校验甚至是修复json数据格式的解决方案。它定义了一整套规则,允许我们通过定义一个schema(本身也是JSON)来描述一个JSON串的数据格式。它有如下优点:

    * 描述你现有的JSON数据的格式;
    * 清晰的、人类/机器可读的文档结构;
    * 完全的结构校验,尤其适用于 自动测试 或 验证客户端提交的数据格式。

    下面为一个定位信息的json schema例子

    //json传输值
    {
      "data" : {
        "id" : 851,
        "detail" : "琴千线长征路-万盛南路附近",
        "area" : 9480,
        "province" : "浙江省",
        "parentArea" : 2819,
        "lng" : 120.32438,
        "district" : "东阳市",
        "lat" : 29.136176,
        "city" : "金华"
      }
    }
    //定位接口返回值的JSON schema
    {
      "type" : "object",
      "properties" : {
        "data" : {
          "type" : "object",
          "properties" : {
            "id" : {
              "type" : "integer",
              "minimum": 0
            },
            "detail" : {
              "type" : "string"
            },
            "area" : {
              "type" : "integer"
            },
            "province" : {
              "type" : "string",
              "pattern" : "^(北京市|天津市|....|浙江省)$"
            },
            "parentArea" : {
              "type" : "integer"
            },
            "lng" : {
              "type" : "number",
              "minimum" : 73,
              "maximum" : 135
            },
            "district" : {
              "type" : "string"
            },
            "lat" : {
              "type" : "number",
              "minimum" : 4,
              "maximum" : 54
            },
            "city" : {
              "type" : "string"
            }
          },
          "required" : [
            "id",
            "detail",
            "area",
            "province",
            "parentArea",
            "lng",
            "district",
            "lat",
            "city"
          ]
        }
      },
      "required" : [
        "data"
      ]
    }

    可以看出:
    1、json schema 本身也是一个json串

    2、每个schema可以描述一个json实例,并且该json实例里每一个节点都可以用一个schema来描述,因此schema与json一样,本身也是一个层级结构,一个schema中可能嵌套着另外若干层schema

    3、json schema 定义的检查规则以数据格式验证为主(字段存在性、字段类型),并可以支持一些简单的数据正确性验证(例如数值范围、字符串的模式等),但不能进行复杂的逻辑校验(例如进价必须小于售价等)。

    JS JSON Schema库

    表1中简要概述了4个JSON Schema库的特性

    表 1. 针对 JavaScript 的 JSON Schema 验证库

    库(作者)草案版本支持库的大概规模
    JSV: JSON Schema 验证器 (Gary Court) draft-01、draft-02 和 draft-03 120KB
    json-schema (Kris Zyp) draft-03 10KB(需要 CommonJS)
    dojox.json.schema (Kris Zyp) draft-02 10KB(需要 Dojo)
    schema.js (Andreas Kalsch) draft-02(部分) 10KB(需要 CommonJS)

    基于 Dojo 的应用程序可能会使用 dojox.json.schema 库,因为该库包含在工具箱中。支持多个版本的(草案)标准的应用程序可能会使用 JSV。

    dojox.json.schema 看上去像是 json-schema 的一个分支,所以它们的用法非常相似。schema.js 只实现 draft-02 的一个子集。所以主要关注的是使用 dojox.json.schema 和 JSV 。

    1. dojox.json.schema的使用

    <html>
    <head>
        <title>dojox.json.schema</title>
        <script src="http://ajax.googleapis.com/ajax/libs/dojo/1.7.0/dojo/dojo.js"
           type="text/javascript"></script>
        <script type="text/javascript">
        require(["dojox/json/schema"], function() {
            // Object to validate
            var successObj = { 
                "foo" : "bar"
            };
            var failureObj = {
                "foo" : 1234
            };
    
            // Schema
            var schema = { 
                "type": "object",
                "properties" : {
                    "foo" : {
                        "type" : "string"
                    }
                }
            };
    
            //var result = dojox.json.schema.validate(failureObj, schema);
            var result = dojox.json.schema.validate(successObj, schema);
    
            // Check results
            if (result.valid) {
                alert("Object is valid");
            } else {
                var errorArr = result.errors;
                alert("property : " + errorArr[0].property + "
    message :  " 
                    + errorArr[0].message);
            }
        });
        </script>
    </head>
    <body>
    Hello, World!
    </body>
    </html>

    dojox.json.schema这种方法只需要引入一个js包,不过必须是线上的,将dojo那个js下载下来后就报错不能执行。

    2. JSV的使用

    <head>
        <title>JSV</title>
        <!-- <script src="https://raw.github.com/garycourt/JSV/master/lib/uri/uri.js" type="text/javascript"></script>
        <script src="https://raw.github.com/garycourt/JSV/master/lib/jsv.js" type="text/javascript"></script>
        <script src="https://raw.github.com/garycourt/JSV/master/lib/json-schema-draft-03.js" type="text/javascript"></script> -->
        <script src="js/uri.js" type="text/javascript"></script>
        <script src="js/jsv.js" type="text/javascript"></script>
        <script src="js/json-schema-draft-03.js" type="text/javascript"></script>
        <script type="text/javascript">
            // Object to validate
            var successObj = { 
                "foo" : "bar"
            };
            var failureObj = {
                "foo" : 1234
            };
    
            // Schema
            var schema = { 
                "type": "object",
                "properties" : {
                    "foo" : {
                        "type" : "string"
                    }
                }
            };
    
            var env = JSV.createEnvironment("json-schema-draft-03"); 
            // validate
            var result = env.validate(successObj, schema);
    
            if (result.errors.length === 0) {
                alert("Object is valid");
            } else {
                var errorArr = result.errors;
                alert("uri : " + errorArr[0].uri + "
    message :  " 
                    + errorArr[0].message);
            }
        </script>
    </head>
    <body>
    Hello, World!
    </body>
    </html>

    JSV这种方法,需要导入3个js包,这是必须下载后才能使用。
    JSV 在 errors 数组中提供了一些高级故障信息。每个错误可能包含以下属性:

    * message:人类可读的错误消息。
    
    
    * uri:失败对象所在位置的 URI。
    
    
    * schemaUri:引起故障的模式的所在位置的 URI。
    
    
    * Attribute:引起故障的模式约束。
    
    
    * Details:包含更多详细信息的自由格式数组,比如预期值。

    使用对比:

    dojox.json.schema只需要引入一个js包,基于 Dojo 的应用程序可能会使用 dojox.json.schema 库,因为该库包含在工具箱中。校验的时候也只需要一行代码即可:var result = dojox.json.schema.validate(successObj, schema); 其中successObj为传入的JSON串,schema为校验规则。

    JSV需要引入三个js包,JSV支持draft-01,draft-02,draft-03三种草案,支持多个版本的(草案)标准的应用程序可能会使用 JSV。校验的时候需要根据草案创建环境,然后再进行校验。var env = JSV.createEnvironment(“json-schema-draft-03”);
    var result = env.validate(successObj, schema); 其中successObj为传入的JSON串,schema为校验规则。JSV在errors数组中提供了一些高级的故障信息,包括message:可读的错误信息;uri:失败对象所在位置的URI;schemaUri:引起故障的模式所在位置的URI;Attribute:引起故障的模式约束;Details:包含更多详细信息的自由格式数组,如果预期值。

    性能对比:

    一共执行50次,成功和失败分开执行,每种情况执行25次。然后记录下每次的执行时间,执行10次,取平均值。
    dojox.json.schema:0.52, 4.28, 3.54, 4, 3.82, 3.64, 3.76, 4.12, 4.16, 5.6
    JSV:4.5, 3.96, 3.88, 3.82, 3.98, 3.96, 3.9, 3.8, 4.1, 4.04

    json schema类型每次执行时间(ms)
    dojox.json.schema 3.744
    JSV 3.994

    发现时间相差不多,JSV由于js包在本地,所以每次时间比较稳定;dojox.json.schema由于需要从网络上去加载js包,导致执行时间有时会波动很大。整体来说,就执行过程,dojox.json.schema要快不少。

    Java JSON Schema库

    表2给出了两种java中使用的JSON Schema库

    库名称地址支持草案
    fge https://github.com/daveclayton/json-schema-validator draft-04 draft-03
    everit https://github.com/everit-org/json-schema draft-04

    建议:

    • 如果在项目中使用了jackson json,那么使用fge是一个好的选择,因为fge就是使用的jackson json。
    • 如果项目中使用的是org.json API,那么使用everit会更好。
    • 如果是使用以上两个库以外的库,那么就使用everit,因为everit会比fge的性能好上两倍。

    fge的使用:

    maven配置

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

    测试代码:

    @Test
    public void testJsonSchema1() {
      JsonNode schema = readJsonFile("src/main/resources/Schema.json");
      JsonNode data = readJsonFile("src/main/resources/failure.json");
      ProcessingReport report = JsonSchemaFactory.byDefault().
                                getValidator().validateUnchecked(schema, data);
      Assert.assertTrue(report.isSuccess());
    }
    
    private JsonNode readJsonFile(String filePath) {
      JsonNode instance = null;
      try {
        instance = new JsonNodeReader().fromReader(new FileReader(filePath));
      } catch (IOException e) {
        e.printStackTrace();
      }
      return instance;
    }

    真正的调用只有一行代码,需要传入验证规则和数据。分别有validate和validateUnchecked两种方法,区别在于validateUnchecked方法不会抛出ProcessingException异常。

    还可以从字符串中读取json,代码如下:

    @Test
    public void testJsonSchema2() {
      String failure = new String("{"foo":1234}");
      String Schema = "{"type": "object", "properties" : {"foo" : {"type" : "string"}}}";
      ProcessingReport report = null;
      try {
        JsonNode data = JsonLoader.fromString(failure);
        JsonNode schema = JsonLoader.fromString(Schema);
        report = JsonSchemaFactory.byDefault().getValidator().validateUnchecked(schema, data);
      } catch (IOException e) {
        e.printStackTrace();
      }
      //Assert.assertTrue(report.isSuccess());
      Iterator<ProcessingMessage> it = report.iterator();
      while (it.hasNext()) {
        System.out.println(it.next());
      }
    }

    其中ProcessingReport对象中维护了一共迭代器,如果执行失败(执行成功时没有信息),其提供了一些高级故障信息。每个错误可能包含以下属性:

    * level: 错误级别(应该就是error)
    * schema:引起故障的模式的所在位置的 URI
    * instance:错误对象
    * domain:验证域
    * keyword:引起错误的约束key
    * found:现在类型
    * expected:期望类型

    以上代码的json信息为:

    failure.json :   {"foo" : 1234}
    
    Schema.json :
    {
      "type": "object",
      "properties" : {
        "foo" : {
          "type" : "string"
        }
      }
    }

    执行错误信息为:

    error: instance type (integer) does not match any allowed primitive type (allowed: ["string"])
        level: "error"
        schema: {"loadingURI":"#","pointer":"/properties/foo"}
        instance: {"pointer":"/foo"}
        domain: "validation"
        keyword: "type"
        found: "integer"
        expected: ["string"]

    everit的使用:

    maven配置(获取最新版本)

    <dependency>
      <groupId>org.everit.json</groupId>
      <artifactId>org.everit.json.schema</artifactId>
      <version>1.3.0</version>
    </dependency>

    测试代码

    @Test
    public void testJsonSchema3() {
      InputStream inputStream = getClass().getResourceAsStream("/Schema.json");
      JSONObject Schema = new JSONObject(new JSONTokener(inputStream));
      JSONObject data = new JSONObject("{"foo" : 1234}");
      Schema schema = SchemaLoader.load(Schema);
      try {
        schema.validate(data);
      } catch (ValidationException e) {
        System.out.println(e.getAllMessage());
      }
    }

    如果验证失败会抛出一个ValidationException异常,然后在catch块中打印出错误信息。everit中的错误信息想比fge来说比较简单,相同的json测试文件,打印的信息如下:

    #/foo: expected type: String, found: Integer

    性能测试:

    1、一共执行1000次,成功和失败分开执行,每种情况执行250次。然后记录下每次的执行时间,执行10次,取平均值。

    fge每1000次的执行时间(ms):1158, 1122, 1120, 1042, 1180, 1254, 1198,1126,1177,1192
    everit每1000次的执行时间(ms):33, 49, 54, 57, 51, 47, 48, 52, 53, 44

    2、一共执行10000次,成功和失败分开执行,每种情况执行2500次。

    方法/场景每次执行时间(ms)
    fge/场景1 1.1569
    fge/场景2 0.3407
    everit/场景1 0.0488
    everit/场景2 0.0206

    使用对比:

    从性能上来说everit完全是碾压fge,官方说的至少两倍,实际测试过程中,差不多有20倍的差距。虽然fge使用的是jackson json,相对来说学习成本可能较低,但是使用下来发现everit的使用也并不复杂,需要注意的是包需要导入正确(org.json)。fge唯一的优势在于错误信息比较详细。还有一点区别在于,everit验证失败是抛出异常,而fge是判断返回一个boolean类型的值。

  • 相关阅读:
    k8s 1.10 部署springcloud
    TF-IDF关键词提取步骤
    k8s 离线安装
    JWT对SpringCloud进行系统认证和服务鉴权
    centos7 安装 docker-registry
    Docker安装elasticsearch-head监控ES步骤
    tengine 配置应用防火墙
    elasticsearch6.1.3 集成分词器
    centos7 nginx tengine 安装
    elk 搭建
  • 原文地址:https://www.cnblogs.com/huanghongbo/p/8628607.html
Copyright © 2011-2022 走看看