JSON Schema

簡介

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  

{
   "type": "string",
   "pattern": "^(\\([0-9]{3}\\))?[0-9]{3}-[0-9]{4}$"
}json

 正則表達式
 enum  {

  "type": "string",
  "enum": ["red", "amber", "green"]
}後端

 枚舉類型
 properties

 說明該JSON對象有哪些屬性/字段數組

"properties": {
  "street_address": { "type": "string" },
  "city": { "type": "string" },
  "state": { "type": "string" }
},

 屬性字段
 definitions

 一般搭配$ref一塊兒說明使用

{ "$ref": "#/definitions/address" }

 自定義字段
 $ref  一般用於複雜說明  引用字段
 required  "required": ["street_address", "city", "state"]  必須字段
 oneof  

{
"oneOf": [
  { "type": "number", "multipleOf": 5 },
  { "type": "number", "multipleOf": 3 }
]
}

// 10 yes

// 9 yes

// 2 no

 知足其中一個
 allof  

{
"allOf": [
  { "type": "string" },
  { "maxLength": 5 }
]
}

// "short" yes

// "too long" no

 知足所有
 multipleOf  { "type": "number", "multipleOf": 5 },  倍數
 not

{ "not": { "type": "string" } }

// 42 yes

//"hello" no 

 取反
 array

{
"type": "array",
  "items": {
  "type": "number"
  }
}

 數組
 propertyNames  正則字符串  定義key的正則規則
 patternProperties  

{
"type":"object",
"patternProperties":{
  "^S_":{"type":"string"}
 }
}

 同時限定key和value
 additionalProperties  boolean 是否容許有格外的屬性 
 dependencies

{
"type":"object",
  "properties":{
    "name":{"type":"string"},
    "age":{"type":"number"}
},
"dependencies":{
  "gender":["age"]
}
}

//{ "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

https://github.com/xeipuuv/gojsonschema

相關文章
相關標籤/搜索