什么是Json Schema ?
Json schema是一種聲明式語言,它可以用來標識Json的結(jié)構(gòu),數(shù)據(jù)類型和數(shù)據(jù)的具體限制,它提供了描述期望Json結(jié)構(gòu)的標準化方法。
利用Json Schema, 你可以定義Json結(jié)構(gòu)的各種規(guī)則,以便確定Json數(shù)據(jù)在各個子系統(tǒng)中交互傳輸時保持兼容和一致的格式。
一般來說,系統(tǒng)可以自己實現(xiàn)邏輯來判斷當前json是否滿足接口要求,比如是否某個字段存在,是否屬性值是有效的。但當驗證需求變得復雜后,比如有大量嵌套json結(jié)構(gòu),屬性之間的復雜關(guān)聯(lián)限制等等,則容易編寫出考慮不全的驗證代碼。另外,當系統(tǒng)需要動態(tài)的json數(shù)據(jù)要求,比如先由用戶自己決定他需要的json結(jié)構(gòu),然后系統(tǒng)根據(jù)用戶表達的定制化json結(jié)構(gòu)需求,幫助用戶驗證后續(xù)的json數(shù)據(jù)。這種系統(tǒng)代碼編譯時無法確定的json結(jié)構(gòu),就需要另一種解決方案。
Json Schema就是針對這種問題的比較自然的解決方案。它可以讓你或你的用戶描述希望的json結(jié)構(gòu)和值的內(nèi)容限制,有效屬性,是否是required, 還有有效值的定義,等等。。利用Json Schema, 人們可以更好的理解Json結(jié)構(gòu),而且程序也可以根據(jù)你的Json Schema驗證Json數(shù)據(jù)。
Json Schema語法的學習見官方介紹。
比如下面的一個簡單例子,用.net下的Json Schema實現(xiàn)庫library LateApexEarlySpeed.Json.Schema進行Json數(shù)據(jù)的驗證:
Json Schema (文件:schema.json):
{
"type": "object",
"properties": {
"propBoolean": {
"type": "boolean"
},
"propArray": {
"type": "array",
"uniqueItems": true
}
}
}
Json 數(shù)據(jù) (文件:instance.json):
{
"propBoolean": true,
"propArray": [ 1, 2, 3, 4, 4 ]
}
C# 代碼:
string jsonSchema = File.ReadAllText("schema.json");
string instance = File.ReadAllText("instance.json");
var jsonValidator = new JsonValidator(jsonSchema);
ValidationResult validationResult = jsonValidator.Validate(instance);
if (validationResult.IsValid)
{
Console.WriteLine("good");
}
else
{
Console.WriteLine($"Failed keyword: {validationResult.Keyword}");
Console.WriteLine($"ResultCode: {validationResult.ResultCode}");
Console.WriteLine($"Error message: {validationResult.ErrorMessage}");
Console.WriteLine($"Failed instance location: {validationResult.InstanceLocation}");
Console.WriteLine($"Failed relative keyword location: {validationResult.RelativeKeywordLocation}");
}
輸出:
Failed keyword: uniqueItems
ResultCode: DuplicatedArrayItems
Error message: There are duplicated array items
Failed instance location: /propArray
Failed relative keyword location: /properties/propArray/uniqueItems
LateApexEarlySpeed.Json.Schema中文介紹
項目原始文檔:https://github.com/lateapexearlyspeed/Lateapexearlyspeed.JsonSchema.Doc
中文文檔:
LateApexEarlySpeed.Json.Schema是2023年12月發(fā)布的一個新的.net下的Json Schema實現(xiàn)庫library,基于截止到2023年12月為止最新版的Json schema - draft 2020.12。
Json Schema驗證功能經(jīng)過了official json schema test-suite for draft 2020.12的測試。(部分排除的用例見下面的已知限制章節(jié))
主要特點:
- 基于微軟.net下默認的System.Text.Json而非經(jīng)典的Newtonsoft.Json
- 高性能:和已有的知名且杰出的.net下的一些JsonSchema library相比,具有很好的性能 (在common case下,利用BenchmarkDotnet進行的性能測試)。用戶請根據(jù)自己的使用場景進行性能驗證
一些性能測試結(jié)果 (下面的測試比較都是在同樣的使用方式下進行,見 性能建議 ):
12th Gen Intel Core i7-12800H, 1 CPU, 20 logical and 14 physical cores
[Host] : .NET 6.0.25 (6.0.2523.51912), X64 RyuJIT AVX2
DefaultJob : .NET 6.0.25 (6.0.2523.51912), X64 RyuJIT AVX2
有效數(shù)據(jù)用例:
Method | Mean | Error | StdDev | Gen0 | Gen1 | Allocated |
---|---|---|---|---|---|---|
ValidateByPopularSTJBasedValidator | 29.80 us | 0.584 us | 0.573 us | 4.4556 | 0.2441 | 55.1 KB |
ValidateByThisValidator | 15.99 us | 0.305 us | 0.300 us | 1.9531 | - | 24.2 KB |
無效數(shù)據(jù)用例:
Method | Mean | Error | StdDev | Median | Gen0 | Gen1 | Allocated |
---|---|---|---|---|---|---|---|
ValidateByPopularSTJBasedValidator | 65.04 us | 2.530 us | 7.341 us | 66.87 us | 4.5776 | 0.1221 | 56.42 KB |
ValidateByThisValidator | 15.47 us | 1.160 us | 3.421 us | 17.14 us | 1.4954 | - | 18.45 KB |
Note: "STJ"是"System.Text.Json"的縮寫,這個是.net sdk里默認帶的json基礎(chǔ)包, LateApexEarlySpeed.Json.Schema這個庫也是基于這個STJ編寫的。
基準Schema:
{
"$id": "http://main",
"type": "object",
"additionalProperties": false,
"patternProperties": {
"propB*lean": {
"type": "boolean"
}
},
"dependentRequired": {
"propNull": [ "propBoolean", "propArray" ]
},
"dependentSchemas": {
"propNull": {
"type": "object"
}
},
"propertyNames": true,
"required": [ "propNull", "propBoolean" ],
"maxProperties": 100,
"minProperties": 0,
"properties": {
"propNull": {
"type": "null"
},
"propBoolean": {
"type": "boolean",
"allOf": [
true,
{ "type": "boolean" }
]
},
"propArray": {
"type": "array",
"anyOf": [ false, true ],
"contains": { "type": "integer" },
"maxContains": 100,
"minContains": 2,
"maxItems": 100,
"minItems": 1,
"prefixItems": [
{ "type": "integer" }
],
"items": { "type": "integer" },
"uniqueItems": true
},
"propNumber": {
"type": "number",
"if": {
"const": 1.5
},
"then": true,
"else": true,
"enum": [ 1.5, 0, 1 ]
},
"propString": {
"type": "string",
"maxLength": 100,
"minLength": 0,
"not": false,
"pattern": "abcde"
},
"propInteger": {
"$ref": "#/$defs/typeIsInteger",
"exclusiveMaximum": 100,
"exclusiveMinimum": 0,
"maximum": 100,
"minimum": 0,
"multipleOf": 0.5,
"oneOf": [ true, false ]
}
},
"$defs": {
"typeIsInteger": { "$ref": "http://inside#/$defs/typeIsInteger" },
"toTestAnchor": {
"$anchor": "test-anchor"
},
"toTestAnotherResourceRef": {
"$id": "http://inside",
"$defs": {
"typeIsInteger": { "type": "integer" }
}
}
}
}
有效基準數(shù)據(jù):
{
"propNull": null,
"propBoolean": true,
"propArray": [ 1, 2, 3, 4, 5 ],
"propNumber": 1.5,
"propString": "abcde",
"propInteger": 1
}
無效基準數(shù)據(jù):
{
"propNull": null,
"propBoolean": true,
"propArray": [ 1, 2, 3, 4, 4 ], // Two '4', duplicated
"propNumber": 1.5,
"propString": "abcde",
"propInteger": 1
}
該實現(xiàn)庫(implementation library)之后可能會transfer成開源項目。
基礎(chǔ)用法
安裝Nuget package
Install-Package LateApexEarlySpeed.Json.Schema
string jsonSchema = File.ReadAllText("schema.json");
string instance = File.ReadAllText("instance.json");
var jsonValidator = new JsonValidator(jsonSchema);
ValidationResult validationResult = jsonValidator.Validate(instance);
if (validationResult.IsValid)
{
Console.WriteLine("good");
}
else
{
Console.WriteLine($"Failed keyword: {validationResult.Keyword}");
Console.WriteLine($"ResultCode: {validationResult.ResultCode}");
Console.WriteLine($"Error message: {validationResult.ErrorMessage}");
Console.WriteLine($"Failed instance location: {validationResult.InstanceLocation}");
Console.WriteLine($"Failed relative keyword location: {validationResult.RelativeKeywordLocation}");
Console.WriteLine($"Failed schema resource base uri: {validationResult.SchemaResourceBaseUri}");
}
輸出信息
當json數(shù)據(jù)驗證失敗后,可以查看錯誤數(shù)據(jù)的具體信息:
-
IsValid: As summary indicator for passed validation or failed validation.
-
ResultCode: The specific error type when validation failed.
-
ErrorMessage: the specific wording for human readable message
-
Keyword: current keyword when validation failed
-
InstanceLocation: The location of the JSON value within the instance being validated. The value is a JSON Pointer.
-
RelativeKeywordLocation: The relative location of the validating keyword that follows the validation path. The value is a JSON Pointer, and it includes any by-reference applicators such as "$ref" or "$dynamicRef". Eg:
/properties/width/$ref/minimum
-
SubSchemaRefFullUri: The absolute, dereferenced location of the validating keyword when validation failed. The value is a full URI using the canonical URI of the relevant schema resource with a JSON Pointer fragment, and it doesn't include by-reference applicators such as "$ref" or "$dynamicRef" as non-terminal path components. Eg:
https://example.com/schemas/common#/$defs/count/minimum
-
SchemaResourceBaseUri: The absolute base URI of referenced json schema resource when validation failed. Eg:
https://example.com/schemas/common
性能建議
盡可能的重用已實例化的JsonValidator實例(JsonValidator可以簡單理解為代表一個json schema驗證文檔)來驗證json數(shù)據(jù),以便獲得更高性能
外部json schema依賴的支持
除了自動支持當前schema文檔內(nèi)的引用關(guān)系,還支持外部json schema依賴:
- 本地schema依賴文本
var jsonValidator = new JsonValidator(jsonSchema);
string externalJsonSchema = File.ReadAllText("schema2.json");
jsonValidator.AddExternalDocument(externalJsonSchema);
ValidationResult validationResult = jsonValidator.Validate(instance);
- 遠程schema url (實現(xiàn)庫將訪問網(wǎng)絡(luò)來獲得遠程的schema)
var jsonValidator = new JsonValidator(jsonSchema);
await jsonValidator.AddHttpDocumentAsync(new Uri("http://this-is-json-schema-document"));
ValidationResult validationResult = jsonValidator.Validate(instance);
自定義keyword的支持
除了json schema specification中的標準keywords之外,還支持用戶創(chuàng)建自定義keyword來實現(xiàn)額外的驗證需求:
{
"type": "object",
"properties": {
"prop1": {
"customKeyword": "Expected value"
}
}
}
ValidationKeywordRegistry.AddKeyword<CustomKeyword>();
[Keyword("customKeyword")] // It is your custom keyword name
[JsonConverter(typeof(CustomKeywordJsonConverter))] // Use 'CustomKeywordJsonConverter' to deserialize to 'CustomKeyword' instance out from json schema text
internal class CustomKeyword : KeywordBase
{
private readonly string _customValue; // Simple example value
public CustomKeyword(string customValue)
{
_customValue = customValue;
}
// Do your custom validation work here
protected override ValidationResult ValidateCore(JsonInstanceElement instance, JsonSchemaOptions options)
{
if (instance.ValueKind != JsonValueKind.String)
{
return ValidationResult.ValidResult;
}
return instance.GetString() == _customValue
? ValidationResult.ValidResult
: ValidationResult.CreateFailedResult(ResultCode.UnexpectedValue, "It is not my expected value.", options.ValidationPathStack, Name, instance.Location);
}
}
internal class CustomKeywordJsonConverter : JsonConverter<CustomKeyword>
{
// Library will input json value of your custom keyword: "customKeyword" to this method.
public override CustomKeyword? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
// Briefly:
return new CustomKeyword(reader.GetString()!);
}
public override void Write(Utf8JsonWriter writer, CustomKeyword value, JsonSerializerOptions options)
{
throw new NotImplementedException();
}
}
Format支持
目前l(fā)ibrary支持如下format:
- uri
- uri-reference
- date
- time
- date-time
- uuid
- hostname
- ipv4
- ipv6
- json-pointer
- regex
Format 驗證需要顯式enable, 當驗證數(shù)據(jù)時,請傳入配置好的 JsonSchemaOptions:
jsonValidator.Validate(instance, new JsonSchemaOptions{ValidateFormat = true});
如果需要自定義format驗證,可以實現(xiàn)一個FormatValidator子類并注冊:文章來源:http://www.zghlxwxcb.cn/news/detail-762411.html
[Format("custom_format")] // this is your custom format name in json schema
public class TestCustomFormatValidator : FormatValidator
{
public override bool Validate(string content)
{
// custom format validation logic here...
}
}
// register it globally
FormatRegistry.AddFormatType<TestCustomFormatValidator>();
Other extension usage doc is to be continued .
限制
- 目前l(fā)ibrary關(guān)注于驗證,暫不支持annotation
- 因為暫不支持annotation, 所以不支持如下keywords: unevaluatedProperties, unevaluatedItems
- 目前不支持 content-encoded string
問題報告
歡迎把使用過程中遇到的問題和希望增加的功能發(fā)到github repo issue中文章來源地址http://www.zghlxwxcb.cn/news/detail-762411.html
More doc is to be written
到了這里,關(guān)于Json Schema簡介和Json Schema的高性能.net實現(xiàn)庫 LateApexEarlySpeed.Json.Schema的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!