zoukankan      html  css  js  c++  java
  • JSON Schema

    简介

    JSON 作为通用的前后端交互,或者后台服务间通信的通用格式被大家广泛使用。我们肯定遇到过一些场景需要校验调用方传递过来的数据格式,比如一定要包含某些字段,某个字段一定要符合某种格式,比如定义了价格的字段,范围一定要在100~200之间,协议字段一定要是TCP或者UDP等枚举类型。你是否在你的用户代码里面自行实现这些判断逻辑呢?如果这样的规则越来越多是不是会显得代码很臃肿呢?这就是为什么要介绍我们今天的主角JSON Schema。JSON Schema定义了JSON格式的规范,各种语言都有开源的第三方JSON Schema校验库,例如Go语言的gojsonschema,这样我们就可以定义一份JSON Schema,然后系统的各个模块都可以复用这套JSON规范,不满足规则的数据JSON Schema会直接报错。

    语法说明

    JSON Schema作为JSON的规范样式,自身也有一套key-value语法用于声明各种规则。

    key value 备注
    $schema

    http://json-schema.org/draft-04/schema#

    http://json-schema.org/draft-06/schema#

    http://json-schema.org/draft-07/schema#

    说明是哪个版本的JSON Schema,不同版本间不完全兼容
    type

    string、number、integer、boolean、object等

    例如{"type":"integer"}说明该字段一定要是整形

    说明字段的类型
     pattern  

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

     正则表达式
     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
    ")
    	} else {
    		fmt.Printf("The document is not valid. see errors :
    ")
    		for _, desc := range result.Errors() {
    			fmt.Printf("- %s
    ", 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

  • 相关阅读:
    ruby学习笔记(5)
    rails学习笔记(2)
    一个不错的rails2.0教程
    rails学习笔记(2)
    rails学习笔记(1)
    一个不错的rails2.0教程
    ruby学习笔记(6)
    ruby学习笔记(6)
    rails学习笔记(1)
    DELPHI的编译指令
  • 原文地址:https://www.cnblogs.com/makelu/p/11828274.html
Copyright © 2011-2022 走看看