JSON 做爲通用的先後端交互,或者後臺服務間通訊的通用格式被你們普遍使用。咱們確定遇到過一些場景須要校驗調用方傳遞過來的數據格式,好比必定要包含某些字段,某個字段必定要符合某種格式,好比定義了價格的字段,範圍必定要在100~200之間,協議字段必定要是TCP或者UDP等枚舉類型。你是否在你的用戶代碼裏面自行實現這些判斷邏輯呢?若是這樣的規則愈來愈可能是不是會顯得代碼很臃腫呢?這就是爲何要介紹咱們今天的主角JSON Schema。JSON Schema定義了JSON格式的規範,各類語言都有開源的第三方JSON Schema校驗庫,例如Go語言的gojsonschema,這樣咱們就能夠定義一份JSON Schema,而後系統的各個模塊均可以複用這套JSON規範,不知足規則的數據JSON Schema會直接報錯。javascript
JSON Schema做爲JSON的規範樣式,自身也有一套key-value語法用於聲明各類規則。html
key | value | 備註 |
$schema | http://json-schema.org/draft-04/schema#java http://json-schema.org/draft-06/schema#git http://json-schema.org/draft-07/schema#github |
說明是哪一個版本的JSON Schema,不一樣版本間不徹底兼容 |
type | string、number、integer、boolean、object等golang 例如{"type":"integer"}說明該字段必定要是整形正則表達式 |
說明字段的類型 |
pattern | { |
正則表達式 |
enum | { "type": "string", |
枚舉類型 |
properties | 說明該JSON對象有哪些屬性/字段數組 "properties": { |
屬性字段 |
definitions | 一般搭配$ref一塊兒說明使用 { "$ref": "#/definitions/address" } |
自定義字段 |
$ref | 一般用於複雜說明 | 引用字段 |
required | "required": ["street_address", "city", "state"] | 必須字段 |
oneof | { // 10 yes // 9 yes // 2 no |
知足其中一個 |
allof | { // "short" yes // "too long" no |
知足所有 |
multipleOf | { "type": "number", "multipleOf": 5 }, | 倍數 |
not | { "not": { "type": "string" } } // 42 yes //"hello" no |
取反 |
array | { |
數組 |
propertyNames | 正則字符串 | 定義key的正則規則 |
patternProperties | { |
同時限定key和value |
additionalProperties | boolean | 是否容許有格外的屬性 |
dependencies | { //{ "gender":"male" } no //{ "gender":"male", "age":18 } yes |
屬性間依賴關係 |
uniqueItems | boolean | 數組元素是否惟一 |
minProperties/maxProperties | number | 最小/大屬性個數 |
定義JSON Schema規則:
{ "$schema": "https://json-schema.org/draft-04/schema#", "type": "object", "properties":{ "schema_version": { "type": "string" }, "service": { "$ref": "#/definitions/service" } }, "additionalProperties": false, "required": [ "schema_version", "service" ], "definitions": { "service": { "type": "object", "properties": { "name": { "$ref": "#/definitions/service_name" }, "runtime": { "$ref": "#/definitions/runtime_location" }, "labels": { "$ref": "#/definitions/service_labels" }, "selector": { "$ref": "#/definitions/service_selector" }, "ports": { "$ref": "#/definitions/service_ports" } }, "required": [ "name", "runtime", "labels", "selector", "ports" ] }, "service_name": { "type": "string", "pattern": "^[a-z0-9-]+.[a-z0-9-]+$" }, "service_labels": { "type": "object", "properties": { "group": { "type": "string" }, "balance_strategy": { "enum": [ "source", "roundrobin", "leastconn" ]} } }, "service_ports": { "type": "array", "uniqueItems": true, "minItems": 1, "items": { "type": "object", "additionalProperties": false, "properties": { "name": { "type": "string" }, "domain_name": { "type": "string" }, "path": { "type": "string" }, "port": { "type": "integer", "minimum": 0, "exclusiveMinimum": true }, "protocol": { "enum": [ "tcp", "udp", "http" ] } }, "required": [ "name", "protocol", "port" ] } }, "label_value": { "type": "string", "pattern": "(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])" }, "version": { "type": "string", "pattern": ",^\\d+(\\.\\d+)+" }, "service_selector": { "type": "object", "propertyNames": { "pattern": "[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*" }, "patternProperties": { "[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*": { "$ref":"#/definitions/label_value" } } }, "runtime_location": { "type": "string", "pattern": "^[a-zA-Z0-9-_]+\\.[a-zA-Z0-9-_]+\\.[a-z0-9-_]+$" } } }
保存爲schema.json文件,後面代碼會用到。
須要校驗的JSON數據爲:
{ "schema_version":"0.1.0", "service":{ "name":"template-service", "runtime":"30007.xx7.abc", "labels":{ "group":"external", "balance_strategy":"roundrobin" }, "selector":{ "podname":"haha4-tester" }, "ports":[ { "name":"http8088", "domain_name":"yyy.xxx.com", "path":"/test/", "port":8088, "protocol":"http" }, { "name":"tcp-00", "port":8800, "protocol":"tcp" } ] } }
保存爲document.json文件。
下面我將用golang的第三方開源庫gojsonschema校驗上面的JSON數據是否符合咱們定義的JSON Schema。
package main import ( "fmt" "github.com/xeipuuv/gojsonschema" "io/ioutil" ) func main() { schemaContent, err := ioutil.ReadFile("schema.json") if err != nil { panic(err.Error()) } jsonContent, err := ioutil.ReadFile("document.json") if err != nil { panic(err.Error()) } loader1 := gojsonschema.NewStringLoader(string(schemaContent)) schema, err := gojsonschema.NewSchema(loader1) if err != nil { panic(err.Error()) } documentLoader := gojsonschema.NewStringLoader(string(jsonContent)) result, err := schema.Validate(documentLoader) if err != nil { panic(err.Error()) } if result.Valid() { fmt.Printf("The document is valid\n") } else { fmt.Printf("The document is not valid. see errors :\n") for _, desc := range result.Errors() { fmt.Printf("- %s\n", desc) } } }
程序運行輸出:
The document is valid
經過上面的介紹和代碼示例,咱們能夠清楚的瞭解到了JSON Schema定義JSON數據規範的便利性和通用性,咱們能夠將JSON Schema應用到咱們的代碼用,減小JSON數據的校驗冗餘代碼。
https://json-schema.org/understanding-json-schema/reference/index.html
https://json-schema.org/learn/getting-started-step-by-step.html