原文地址: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 + "\nmessage : "
+ 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 + "\nmessage : "
+ 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类型的值。

利用JsonSchema校验json数据内容的合规性(转)的更多相关文章

  1. Struts2.5 利用Ajax将json数据传值到JSP

    AJAX +JSON=>JSP AJAX AJAX 是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术. 通过在后台与服务器进行少量数据交换,AJAX 可以使网页实现异步更新.这意味着 ...

  2. 利用Gson将JSON数据进行格式化(pretty print)

    我们可以利用Gson包将String类型的JSON数据进行格式化. Gson gson = new GsonBuilder().setPrettyPrinting().create(); JsonPa ...

  3. 利用tempo将json数据填充到html模板

    1.下载tempo 2.使用 <!DOCTYPE html> <html> <head lang="zn-ch"> <meta chars ...

  4. 利用AXIS2传递JSON数据

    Axis2是目前比较流行的WebService引擎.WebService被应用在很多不同的场景.例如,可以使用WebService来发布服务端 Java类的方法,以便使用不同的客户端进行调用.这样可以 ...

  5. SpringBoot 03_利用FastJson返回Json数据

    自上一节:SpringBoot 02_返回json数据,可以返回json数据之后,由于有些人习惯于不同的Json框架,比如fastjson,这里介绍一下如何在SpringBoot中集成fastjson ...

  6. 利用Python读取json数据并求数据平均值

    要做的事情:一共十二个月的json数据(即12个json文件),json数据的一个单元如下所示.读取这些数据,并求取各个(100多个)城市年.季度平均值. { "time_point&quo ...

  7. 利用json2html将json数据填充到html模板

    1.下载json2html>> 2.制作好模板.准备好json数据.启动 <!DOCTYPE html> <html> <head> <meta ...

  8. 利用请求的JSON数据创建图形图层

    先看效果图: 包含三个部分:信息窗口(标题是要素的某个属性信息,其余是感兴趣的属性信息).图上图形按照某一属性大小不一显示,图例 1.创建底图用于存放以上三部分: "esri/Map&quo ...

  9. (三十六)利用AFNetworking进行JSON数据解析

    1.首先要安装CocoaPods,然后在需要AFNetworking的工程根目录建立podfile,内容如下: platform :ios, '7.0' pod 'AFNetworking' 2.然后 ...

随机推荐

  1. Ubuntu 14.04 上安装 Gurobi

    参考: Installing Gurobi 7 on Linux Ubuntu 14.04 上安装 Gurobi 1.在gurobi上创建一个账户,并登陆: 2.从gurobi optimizer上下 ...

  2. 4、lvs nat和dr类型演示

    实战操作 LVS-NAT  (应用场景:VIP是公网地址,DIP和RIP一般使用私网地址,NAT的主要目的是为了隐藏服务器) 核心要点: 1.DIP与各real server的RIP必须在同一个网段中 ...

  3. SAP按销售订单生产和标准结算配置及操作手册

    SAP按销售订单生产和标准结算配置及操作手册 http://blog.sina.com.cn/s/blog_6787c0b80101a3tl.html SAP按销售订单生产和标准结算配置及操作手册 S ...

  4. 给 layui upload 带每个文件的进度条, .net 后台代码

    1.upload.js 扩展 功能利用ajax的xhr属性实现该功能修改过modules中的upload.js文件功能具体实现:在js文件中添加监听函数 //创建监听函数 var xhrOnProgr ...

  5. Educational Codeforces Round 23 D. Imbalanced Array 单调栈

    D. Imbalanced Array time limit per test 2 seconds memory limit per test 256 megabytes input standard ...

  6. Codeforces 797B - Odd sum

    B. Odd sum 题目链接:http://codeforces.com/problemset/problem/797/B time limit per test 1 second memory l ...

  7. 日常英语---十四、Dolce & Gabbana cancels China show amid 'racist' ad controversy(adj.温柔的,prep.在其中)

    日常英语---十四.Dolce & Gabbana cancels China show amid 'racist' ad controversy(adj.温柔的,prep.在其中) 一.总结 ...

  8. (转)c# 断言类

    Assert 类 使用 true/false 命题验证单元测试中的条件. 继承层次结构 System.Object Microsoft.VisualStudio.TestTools.UnitTesti ...

  9. SSD固态硬盘是会掉速的。

    也没什么好的办法. 只是自己不再疑神疑鬼,总觉得中病毒了. 下面的文章还是挺有参考意义的. http://diy.pconline.com.cn/627/6271636_all.html SSD变慢了 ...

  10. socket 发送图片

    using System;using System.Collections.Generic;using System.Text;using System.Net.Sockets;using Syste ...