最近項目中遇到了這樣的需求,需要對上傳的Json進行驗證,以確保Json數據的准確性。前后使用了兩種方式來驗證:
(1)第一種方式的實現思想:根據Json數據的格式,嚴格定義相應的類結構,並在System.Runtime.Serialization命名空間下的DataContractAttribute、DataMemberAttribute對class和property進行標注,如果property是必須提供的,則在Property上添加[DataMember(IsRequired = true)]。
然后使用Newtonsoft.Json程序集中的JsonConvert.DeserializeObject<T>()方法加需要驗證的JsonContent反序列化一下,並用try...catch包含反序列化語句,如果轉換成功,則表明JsonContent格式滿足要求。如果轉換不成功,則會拋出異常,有catch接住。
try { var obj = JsonConvert.DeserializeObject<SkuConfigEntity>(skuManifest, new JsonSerializerSettings() { Formatting = Formatting.Indented, NullValueHandling = NullValueHandling.Ignore, MissingMemberHandling = MissingMemberHandling.Error }); } catch(Exception e) { return false; }
這種方法是最初使用的方法,個人認為不完善,也不夠完美,轉換出錯的情況加,exception中不能給出具體的出錯原因。
(2)第二種實現思想:Json Schema(Json 模式)。在前輩的指點下,我知道了Json Schema,
JSON 模式是一種基於 JSON 格式定義 JSON 數據結構的規范。它被寫在 IETF 草案下並於 2011 年到期。JSON 模式:
- 描述現有數據格式。
- 干凈的人類和機器可讀的文檔。
- 完整的結構驗證,有利於自動化測試。
- 完整的結構驗證,可用於驗證客戶端提交的數據。
JSON 模式驗證庫
目前有好幾個驗證器可用於不同的編程語言。但是目前最完整和兼容 JSON 模式的驗證器是 JSV。
語言 | 程序庫 |
---|---|
C | WJElement (LGPLv3) |
Java | json-schema-validator (LGPLv3) |
.NET | Json.NET (MIT) |
ActionScript 3 | Frigga (MIT) |
Haskell | aeson-schema (MIT) |
Python | Jsonschema |
Ruby | autoparse (ASL 2.0); ruby-jsonschema (MIT) |
PHP | php-json-schema (MIT). json-schema (Berkeley) |
JavaScript | Orderly (BSD); JSV; json-schema; Matic (MIT); Dojo; Persevere (modified BSD or AFL 2.0); schema.js. |
JSON 模式示例
下面是一個基本的 JSON 模式,其中涵蓋了一個經典的產品目錄說明:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Product",
"description": "A product from Acme's catalog",
"type": "object",
"properties": {
"id": {
"description": "The unique identifier for a product",
"type": "integer"
},
"name": {
"description": "Name of the product",
"type": "string"
},
"price": {
"type": "number",
"minimum": 0,
"exclusiveMinimum": true
}
},
"required": ["id", "name", "price"]
}
我們來看一下可以用於這一模式中的各種重要關鍵字:
關鍵字 | 描述 |
---|---|
$schema | $schema 關鍵字狀態,表示這個模式與 v4 規范草案書寫一致。 |
title | 用它給我們的模式提供了標題。 |
description | 關於模式的描述。 |
type | type 關鍵字在我們的 JSON 數據上定義了第一個約束:必須是一個 JSON 對象。 |
properties | 定義各種鍵和他們的值類型,以及用於 JSON 文件中的最小值和最大值。 |
required | 存放必要屬性列表。 |
minimum | 給值設置的約束條件,表示可以接受的最小值。 |
exclusiveMinimum | 如果存在 "exclusiveMinimum" 並且具有布爾值 true,如果它嚴格意義上大於 "minimum" 的值則實例有效。 |
maximum | 給值設置的約束條件,表示可以接受的最大值。 |
exclusiveMaximum | 如果存在 "exclusiveMinimum" 並且具有布爾值 true,如果它嚴格意義上小於 "maximum" 的值則實例有效。 |
multipleOf | 如果通過這個關鍵字的值分割實例的結果是一個數字則表示緊靠 "multipleOf" 的數字實例是有效的。 |
maxLength | 字符串實例字符的最大長度數值。 |
minLength | 字符串實例字符的最小長度數值。 |
pattern | 如果正則表達式匹配實例成功則字符串實例被認為是有效的。 |
上述的Json模式可以驗證如下Json串。
[ { "id": 2, "name": "An ice sculpture", "price": 12.50 }, { "id": 3, "name": "A blue mouse", "price": 25.50 } ]
在.Net中,Json Schema 通過Newtonsoft.Json.Schema命名空間下的JsonSchema類可以實現。
每一個JsonSchema實例表示對一個Property的描述以及限制。
JsonSchema class的幾個關鍵屬性:
Type | 設置Property的類型 | |
Required | 設置Property是否為必須的 | |
Properties | 包含的所有下一級Property的JsonSchema,該屬性是Dictionary<string,JsonSchema>類型,添加子項之前需初始化。 | |
Items | 包含的所有下一級的數組項的JsonSchema,該屬性是IList<JsonSchema>類型,添加子項之前需要初始化。 | |
使用Newtonsoft.Json.Schema命名空間下的IsValid()擴展方法,可實現對Json串的驗證,該方法是JToken的擴展方法。
public static JsonSchema GetSkuJsonSchema()
{
JsonSchema rootSchema = new JsonSchema() { Title = "Sku.Json", Type = JsonSchemaType.Object };
rootSchema.Properties = new Dictionary<string, JsonSchema>(); rootSchema.Properties.Add("name", new JsonSchema() { Type = JsonSchemaType.String, Required = true }); rootSchema.Properties.Add("displayName", new JsonSchema() { Type = JsonSchemaType.String, Required = true }); rootSchema.Properties.Add("apimInstance", new JsonSchema() { Type = JsonSchemaType.String, Required = true }); rootSchema.Properties.Add("apiPath", new JsonSchema() { Type = JsonSchemaType.String, Required = true }); JsonSchema skuSchema = new JsonSchema() { Title = "skus", Type = JsonSchemaType.Array, Required = true }; skuSchema.Properties= new Dictionary<string, JsonSchema>(); skuSchema.Properties.Add("name", new JsonSchema() { Type = JsonSchemaType.String, Required = true }); skuSchema.Properties.Add("tier", new JsonSchema() { Type = JsonSchemaType.String, Required = true }); skuSchema.Properties.Add("subscriptionSkuQuota", new JsonSchema() { Type = JsonSchemaType.Integer, Required = false }); skuSchema.Properties.Add("skutype", new JsonSchema() { Type = JsonSchemaType.String, Required = false }); skuSchema.Properties.Add("skuquota", new JsonSchema() { Type = JsonSchemaType.String, Required = false }); skuSchema.Properties.Add("apimProductId", new JsonSchema() { Type = JsonSchemaType.String, Required = false }); JsonSchema locationsSchema = new JsonSchema() { Title = "locations", Type = JsonSchemaType.Array, Required = true }; locationsSchema.Properties= new Dictionary<string, JsonSchema>(); locationsSchema.Properties.Add("location", new JsonSchema() { Type = JsonSchemaType.String, Required = true }); locationsSchema.Properties.Add("apimProductId", new JsonSchema() { Type = JsonSchemaType.String, Required = false }); skuSchema.Properties.Add("locations", locationsSchema); JsonSchema meterIdsSchema = new JsonSchema() { Title = "meterIds", Type = JsonSchemaType.Array, Required = true }; meterIdsSchema.Items = new List<JsonSchema>(); meterIdsSchema.Items.Add(new JsonSchema() { Type = JsonSchemaType.String, Required = false }); skuSchema.Properties.Add("meterIds", meterIdsSchema); JsonSchema requiredFeaturesSchema = new JsonSchema() { Title = "requiredFeatures", Type = JsonSchemaType.Array, Required = true }; requiredFeaturesSchema.Items = new List<JsonSchema>(); requiredFeaturesSchema.Items.Add(new JsonSchema() { Type = JsonSchemaType.String, Required = false }); skuSchema.Properties.Add("requiredFeatures", requiredFeaturesSchema); rootSchema.Properties.Add("skus", skuSchema); return rootSchema;
}
JToken jtoken=JToken.Parse(jsonContent);
IList<string> errorList;
var result=jtoken.IsValid(schema, out errorList);
如果驗證成功,則返回ture,如果驗證失敗,則返回false,並且errorList中會包含詳細的錯誤信息。