快速理解json schema是什么

5,494 阅读5分钟

首先介绍一下JSON Schema ,它是用于验证 JSON 数据结构的强大工具,JSON Schema是以一个JSON串来描述的JSON数据规范,可以用JSON Schema检验一个给定的JSON串是否满足约定的数据规范。目前工作中的http接口的输入输出多数都是JSON格式的数据,校验数据格式是否满足约定是必不可少的,无论是业务代码中对输入数据进行校验,还是测试用例中对接口输出数据做校验,都可以通过JSON Schema完成。下面通过几个例子看看JSON Schema到底是个啥。

例子1:最简单的JSON Schema就是没有限制

虽然我们使用JSON Schema 的目的是对JSON格式的数据做种种限制和校验,但空对象是完全有效的模式,可以接受任何有效的JSON。

{}

下面这些都是有效的数据

31

"I'm a string"

{ "an": [ "arbitrarily", "nested" ], "data": "structure" }

例子2: JSON Schema的限制条件

为JSON Schema加几个限制条件,看看下面这个JSON Schema是如何约定JSON数据的

{
	"type": "object",
	"properties": {
		"number": {
			"type": "number"
		},
		"street_name": {
			"type": "string"
		},
		"street_type": {
			"type": "string",
			"enum": ["Street", "Avenue", "Boulevard"]
		}
	},
	"required": ["number", "street_name"]
}
  • type关键字表示JSON 限定类型是object,每个属性的type表示这个属性的数据类型
  • properties关键字指定这个object有三个属性number,street_name,street_type,
  • enum关键字表示这个street_type的数据只能是"Street", "Avenue", "Boulevard"这三个值
  • required 表示number,street_name是必须有的属性,默认情况下,由properties关键字定义的属性不是必需的。

可以校验通过的数据

{
	"number": 1600,
	"street_name": "Pennsylvania",
	"street_type": "Avenue"
}
{
	"number": 1600,
	"street_name": "Pennsylvania"
}

 

不能校验通过的数据

 

缺少必须属性

{ "number": 1600 }

 

street_type使用的值“super-speed”不是在JSON Schema中约定的枚举值

{
	"number": 1600,
	"street_name": "Pennsylvania",
	"street_type": "super-speed"
}

关键字介绍

JSON Schema通过关键字描述对JSON数据的限制条件,下面再介绍几个关键字进一步了解JSON Schema 的功能。

 

1. 用来定义基本功能的关键字

$schema: 这个关键字是JSON Schema的方言标识符,用于声明schema使用的是哪个draft版本。

{ "$schema": "json-schema.org/draft-07/sc… }

 

$id: 声明一个模式资源标识

{ "$id":"yourdomain.com/schemas/you…

 

type: 声明数据类型,可选的内容包括:

  • string
  • number (整数、浮点数)
  • integer (整数)
  • object
  • array
  • boolean
  • null

2. 用于string类型验证的关键字

可以用下面这些关键字去限制string类型的数据

  • minLength,字符串最小长度,非负数
  • maxLength,字符串最大长度,非负数
  • pattern,正则表达式

{ "type":"string", "maxLength":10 }

 

3. 用于Number类型验证的关键字

  • maximum: 限定最大值小于等于给定的值
  • minimum: 限定最小值大于等于给定的值
  • exclusiveMaximum: 限定最大值小于给定的值
  • exclusiveMinimum: 限定最小值大于给定的值
  • multipleOf: 限制数据为给定数字的倍数。

下面例子约定值是10的倍数都可以校验通过。

{ "type":"number", "multipleOf":10 }

 

4. 用于Array类型验证的关键字

数组类型的限制分为两种:items模式和contains模式
items模式:任意长度的序列,其中每个项目都匹配相同的模式。

{
  "type": "array",
  "minItems": 2,
  "maxItems": 3
}

{
  "type": "array",
  "uniqueItems": true
}
  • minItems: 限定数组长度大于等于给定的值
  • maxItems: 限定数组长度小于等于给定的值
  • uniqueItems: 限定数组中元素是唯一的

 

contains模式只需要针对数组中的一个或多个项目进行验证。

{
  "type": "array",
  "contains": {
    "type": "number"
  },
  "minContains": 2,
  "maxContains": 3
}
  • minContains: 限定最少匹配contains的次数
  • maxContains: 限定最多匹配contains的次数

 

验证失败的数据

["apple", "orange", 2]
["apple", "orange", 2, 4, 8, 16]

验证通过的数据

["apple", "orange", 2, 4]
["apple", "orange", 2, 4, 8]number还可以使用multipleOf关键字将数字限制为给定数字的倍数。它可以被设置为任何正数。

5. 用于Object类型验证的关键字

{
  "type": "object",
  "properties": {
    "name": { "type": "string" },
    "email": { "type": "string" },
    "address": { "type": "string" },
    "telephone": { "type": "string" }
  },
  "required": ["name", "email"]
}
{
  "type": "object",
  "minProperties": 2,
  "maxProperties": 3
}

  • minProperties: 限定最小属性个数
  • maxProperties: 限定最大属性个数
  • required: 必须有得属性
  • properties: 用来配置对象属性

 

Java中利用JSON Schema校验数据

下面我们用一个简单的例子演示一下JSON Schema在java程序中校验数据

首先配置依赖

  1. jsonschema-generator是一个根据java object生成JSON Schema的工具,支持Draft 6, 7, 2019-09, 2020-12
  2. everit-json-schema是官方推荐的一个校验工具
        <dependency>
            <groupId>com.github.victools</groupId>
            <artifactId>jsonschema-generator</artifactId>
            <version>4.31.0</version>
        </dependency>
		<dependency>
            <groupId>com.github.erosb</groupId>
            <artifactId>everit-json-schema</artifactId>
            <version>1.14.2</version>
        </dependency>

再准备一个JSON Schema

约定输入的JSON数据的目的实际是要用这个数据转化成java对象,所以我们用一个java对象生成一个JSON Schema

public class Person {
    private String name;
    private int idNumber;
    private int phoneNumber;
    private List<String> qualifications;
    private Address address;
    private Gender gender;
}

public class Address {
    private String firstLine;
    private String secondLine;
    private String thirdLine;
}

public enum Gender {
    male,female
}

public void schemaGenerator() {
    SchemaGeneratorConfigBuilder configBuilder = new SchemaGeneratorConfigBuilder(SchemaVersion.DRAFT_7, OptionPreset.PLAIN_JSON);
    SchemaGeneratorConfig config = configBuilder.build();
    SchemaGenerator generator = new SchemaGenerator(config);
    JsonNode jsonSchema = generator.generateSchema(Person.class);

    System.out.println(jsonSchema.toPrettyString());
}

 

这样我们就获得了一个JSON Schema

{
  "$schema" : "<http://json-schema.org/draft-07/schema#",>
  "type" : "object",
  "properties" : {
    "address" : {
      "type" : "object",
      "properties" : {
        "firstLine" : {
          "type" : "string"
        },
        "secondLine" : {
          "type" : "string"
        },
        "thirdLine" : {
          "type" : "string"
        }
      }
    },
    "qualifications" : {
      "type" : "array",
      "items" : {
        "type" : "string"
      }
    },
    "gender" : {
      "type" : "string",
      "enum" : [ "male", "female" ]
    },
    "idNumber" : {
      "type" : "integer"
    },
    "name" : {
      "type" : "string"
    },
    "phoneNumber" : {
      "type" : "integer"
    }
  }
}

 

接下来我们准备一个用于验证的JSON数据,这是一个不符合JSON Schema约定的数据

{
  "name": "tom",
  "idNumber": 0,
  "phoneNumber": 0,
  "qualifications": null,
  "address": {
    "firstLine": "beijing",
    "secondLine": null,
    "thirdLine": null
  },
  "gender": null
}

 

校验数据

    @Test
    public void validateDraft7Demo(){
        String schemaJsonString = getJsonString("schema2.json");
        String data = getJsonString("person.json");
        SchemaLoader loader = SchemaLoader.builder()
                .schemaJson(new JSONObject(schemaJsonString))
                .draftV7Support()
                .build();
        Schema schema = loader.load().build();
        try {
            schema.validate(new JSONObject(data));
        } catch (ValidationException validationException) {
            throw new JsonValidationException(validationException);
        }
    }
public class JsonValidationException extends RuntimeException {

    private ValidationException validationException;

    public JsonValidationException(ValidationException validationException) {
        this.validationException = validationException;
    }

    @Override
    public String getMessage() {
        return validationException.getAllMessages().toString();
    }

}

 

执行结果中包含了输入数据包含的6个不符合约定的地方

JsonValidationException: [
    #/qualifications: expected type: JSONArray, found: Null, 
    #/address/thirdLine: expected type: String, found: Null, 
    #/address/secondLine: expected type: String, found: Null, 
    #/address/firstLine: expected type: String, found: Null, 
    #/gender: null is not a valid enum value, 
    #/gender: expected type: String, found: Null
    ]

结语

JSON Schema提供了数据规范语法,JSON Schema 标准经过修订有多个Draft,当前的版本是Draft 202-12,Draft版本不同会略有差异。JSON Schema可以用于校验接口输入数据、自动化测试、甚至可以用其生成代码,jsonschema2pojo就是一个这样的工具,利用JSON Schema在数据约束和校验方面可以提供一些便利。

参考

JSON Schema