原文地址:Json schemajavascript
複雜的AJAX應用程序能夠與數百個不一樣的JSON服務進行交互,所以,引入對客戶端驗證的需求。html
在處理校驗問題方面有着不少的工具,可是一般能夠將它們歸爲如下幾類:java
* 判斷數據是否已被正確格式化 * 手動檢查形式有誤的數據並嘗試糾正 * 手動檢查形式有誤的數據並將有誤數據丟棄 * 自動檢查形式有誤的數據
在這裏只討論自動校驗方面的可用工具包中的json schema,son schema項目首頁:http://json-schema.org/git
JSON schema是一個幫助你定義、校驗甚至是修復json數據格式的解決方案。它定義了一整套規則,容許咱們經過定義一個schema(自己也是JSON)來描述一個JSON串的數據格式。它有以下優勢:github
* 描述你現有的JSON數據的格式; * 清晰的、人類/機器可讀的文檔結構; * 徹底的結構校驗,尤爲適用於 自動測試 或 驗證客戶端提交的數據格式。
下面爲一個定位信息的json schema例子ajax
//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" ] }
能夠看出:
一、json schema 自己也是一個json串json
二、每一個schema能夠描述一個json實例,而且該json實例裏每個節點均可以用一個schema來描述,所以schema與json同樣,自己也是一個層級結構,一個schema中可能嵌套着另外若干層schemaapi
三、json schema 定義的檢查規則以數據格式驗證爲主(字段存在性、字段類型),並能夠支持一些簡單的數據正確性驗證(例如數值範圍、字符串的模式等),但不能進行復雜的邏輯校驗(例如進價必須小於售價等)。數組
表1中簡要概述了4個JSON Schema庫的特性網絡
表 1. 針對 JavaScript 的 JSON Schema 驗證庫
庫(做者) | 草案版本支持 | 庫的大概規模 |
---|---|---|
JSV: JSON Schema 驗證器 (Gary Court) | draft-0一、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要快很多。
表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 |
建議:
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"]
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
一、一共執行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
二、一共執行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類型的值。