"productId": 1,
"productName": "A green door",
"price": 12.50,
"tags": [ "home", "green" ]
}

JsonSchema 示例:

{
"$schema": "https://json-schema.org/draft/2020-12/schema", // schema 規范版本,可以沒有
"$id": "https://example.com/product.schema.json", // json schema 地址,可以沒有
"title": "Product", // json schema 的標題信息,可以沒有
"description": "A product from Acme's catalog", // json schema 的描述信息,可以沒有
"type": "object", // 數據類型
"properties": { // 對象屬性信息
"productId": { //屬性名稱
"description": "The unique identifier for a product", // 屬性描述
"type": "integer" // 屬性類型
},
"productName": { //屬性名稱
"description": "Name of the product", // 屬性描述
"type": "string" // 屬性類型
},
"price": { //屬性名稱
"description": "The price of the product", // 屬性描述
"type": "number", // 屬性類型
"exclusiveMinimum": 0 // 約束最小值不能小于0
},
"tags": { // 屬性名稱
"description": "Tags for the product", // 屬性描述
"type": "array", // 屬性類型
"items": {
"type": "string" // 屬性類型
},
"minItems": 1, // 約束條件,至少要有一個元素
"uniqueItems": true // 不能有重復項
}
},
"required": [ "productId", "productName", "price" ] // 必須的屬性,不存在則不符合 schema 的約束
}

JSON Schema 的核心定義了以下基本類型:

除了上面的這些驗證類型,還有很多驗證,具體可以參考:https://json-schema.org/draft/2020-12/json-schema-validation.html

Pracetice

我選擇的是 JsonSchema.Net,這個是基于 System.Text.Json 來實現的一個 Json schema 的擴展,使用起來也還好,上手也比較簡單

構建 json schema 的簡單示例:

var jsonSchema = new JsonSchemaBuilder()
.Properties(
("name", new JsonSchemaBuilder()
.Type(SchemaValueType.String)
.MinLength(1)
.MaxLength(10)
),
("age", new JsonSchemaBuilder()
.Type(SchemaValueType.Number)
.Minimum(1)
)
)
.Required("name")
.Build();

這個示例構建了一個簡單的 json 對象,這個對象有兩個屬性一個 name 一個 age,其中 name 是必須的屬性,

name 是一個最小長度為1,最大長度為 10 的字符串,age 是一個最小值為 1 的數字

除了使用?JsonSchemaBuilder?自己構建一個 json schema,現在有很多在線的基于一段 json 自動生成 json schema 的工具,我們也可以從一個 json schema 文件或者一個 Stream或者一段 schema 文本來獲取一個 json schema,本質就是讀取一段 json 反序列成了一個 json schema 對象

const string testJsonSchema = @"
{
""$schema"": ""https://json-schema.org/draft/2020-12/schema"",
""type"": ""object"",
""properties"": {
""Data"": {
""type"": ""array"",
""items"":
{
""type"": ""object"",
""properties"": {
""NoticeTitle"": {
""type"": ""string""
},
""NoticeCustomPath"": {
""type"": ""string""
},
""NoticePublishTime"": {
""type"": ""string""
}
},
""required"": [
""NoticeTitle"",
""NoticeCustomPath"",
""NoticePublishTime""
]
}
},
""PageNumber"": {
""type"": ""integer""
},
""PageSize"": {
""type"": ""integer""
},
""TotalCount"": {
""type"": ""integer""
},
""PageCount"": {
""type"": ""integer""
},
""Count"": {
""type"": ""integer""
}
},
""required"": [
""Data"",
""PageNumber"",
""PageSize"",
""TotalCount"",
""PageCount"",
""Count""
]
}
";
var schema = JsonSchema.FromText(testJsonSchema);

有了 json schema 之后我們就可以用來驗證 json 是否合法了,JsonSchema 中有一個?ValidationResults Validate(JsonElement root, ValidationOptions? options = null)?的方法

在 2.2.0 之前的版本你需要將 json 轉換為?JsonElement?來進行驗證,下面是文檔給出的示例,你需要先獲取獲取一個?JsonDocument,然后使用?JsonDocument?的?RootElement?來驗證

JsonSchema schema = new JsonSchemaBuilder()
.Properties(
(
"myProperty", new JsonSchemaBuilder()
.Type(SchemaValueType.String)
.MinLength(10)
)
)
.Required("myProperty");
var emptyJson = JsonDocument.Parse("{}").RootElement;
var booleanJson = JsonDocument.Parse("{\"myProperty\":false}").RootElement;
var stringJson = JsonDocument.Parse("{\"myProperty\":\"some string\"}").RootElement;
var shortJson = JsonDocument.Parse("{\"myProperty\":\"short\"}").RootElement;
var numberJson = JsonDocument.Parse("{\"otherProperty\":35.4}").RootElement;
var nonObject = JsonDocument.Parse("\"not an object\"").RootElement;

var emptyResults = schema.Validate(emptyJson);
var booleanResults = schema.Validate(booleanJson);
var stringResults = schema.Validate(stringJson);
var shortResults = schema.Validate(shortJson);
var numberResults = schema.Validate(numberJson);
var nonObjectResults = schema.Validate(nonObject);

感覺這樣太不方便,于是就寫了兩個擴展方法來方便直接從?JsonDocument?和?string?來驗證

public static ValidationResults Validate(this JsonSchema jsonSchema, JsonDocument jsonDocument, ValidationOptions? validationOptions = null)
{
return jsonSchema.Validate(jsonDocument.RootElement, validationOptions);
}

public static ValidationResults Validate(this JsonSchema jsonSchema, string jsonString, ValidationOptions? validationOptions = null)
{
using var jsonDocument = JsonDocument.Parse(jsonString);
return jsonSchema.Validate(jsonDocument, validationOptions);
}

并且提了 PR,現在使用 2.2.0 版本就可以直接用了

var validateResults = schema.Validate("{}");
WriteLine(validateResults.IsValid);

返回的結果中?IsValid?就代表了這個 Json 是否符合這個 json schema 的約束,true?就是滿足約束,false?就是不滿足

默認的驗證結果,不會返回具體的錯誤信息,你可以指定一個 ValidationOption,指定?OutputFormat?為?Detailed?來返回具體的錯誤信息

var schema = JsonSchema.FromText(testJsonSchema);

var validationOptions = new ValidationOptions()
{
OutputFormat = OutputFormat.Detailed
};

var invalidJson = @"{
""Data"": [
{
""NoticeExternalLink"": null
}
],
""PageNumber"": 1,
""PageSize"": 10,
""TotalCount"": 5,
""PageCount"": 1,
""Count"": 5
}
";

var validateResult = schema.Validate(invalidJson, validationOptions);
WriteLine(validateResult.IsValid);
WriteLine(validateResult.Message);

輸出結果如下:

False
Required properties [NoticeTitle, NoticeCustomPath, NoticePublishTime] were not present

驗證 API 返回結果:

using var httpClient = new HttpClient();
var result = await httpClient.GetStringAsync("http://reservation.weihanli.xyz/api/notice");
validateResult = schema.Validate(result, validationOptions);
WriteLine(validateResult.IsValid);
WriteLine(validateResult.Message);

More

這個庫的作者除了實現了 JsonSchema 的支持,還提供了對于 JsonPath、JsonPatch 等支持,有需要的可以關注一下 https://github.com/gregsdennis/json-everything

作者還提供了一個擴展庫,可以基于強類型的 Model 直接生成一個 schema,不需要再自己構建 schema

除了這個庫你也可以選擇別的庫來實現,Newtonsoft.Json.Schema 是基于 Newtosoft.Json 實現的 JsonSchema 的支持,也可以嘗試一下

References

文章轉自微信公眾號@amazingdotnet

上一篇:

vue3 實現簡單頁面登錄,fastapi + mysql 前后端分離,pinia登錄狀態持久化

下一篇:

Django-Tastypie庫詳解:快速構建RESTful API的Django之道
#你可能也喜歡這些API文章!

我們有何不同?

API服務商零注冊

多API并行試用

數據驅動選型,提升決策效率

查看全部API→
??

熱門場景實測,選對API

#AI文本生成大模型API

對比大模型API的內容創意新穎性、情感共鳴力、商業轉化潛力

25個渠道
一鍵對比試用API 限時免費

#AI深度推理大模型API

對比大模型API的邏輯推理準確性、分析深度、可視化建議合理性

10個渠道
一鍵對比試用API 限時免費