Yaml校验,使用Java校验yaml文件

Yaml校验

1. 核心目的和研究方向

项目中用到了yaml,例如:

# 字符串
name: jack
# 0-200岁,正数
age: 18

yaml需要客户填写,但是客户可能不会按照规则填写,所以需要定义一个schema文件来校验,例如:

name: 
	type: string
age:
	type: integer
	min: 0
	max: 200

本文涉及

  1. 是否存在这样的schema?以及schema如何编写?
  2. 如何使用Java项目进行yaml文件的验证

2. 是否存在这样的schema?以及schema如何编写?

2.1 是否存在这样的Schema

存在,以下2种

名字说明优点缺点参考文档Java库
JsonSchema用于校验标准Json数据格式的一种Schema,简单的yaml可以使用JsonSchema来进行校验。标准丰富、支持的库多、支持的语言多有些Yaml的扩展语法、JsonSchema可能无法验证https://json-schema.org/learn/getting-started-step-by-step
YamlSchema专门用于校验Yaml数据格式的Schema原生支持Yaml、支持扩展语法支持的编程语言少、库少https://asdf-standard.readthedocs.io/en/1.0.3/schemas/yaml_schema.html

2.2 Schema如何编写

com.networknt.json-schema-validator库为例子,

以如下文件为例子:

# 数字类型,范围为0-50
number_field: 25
# 字符串类型
string_field: "some string"
# 数组类型
array_field: ["item1", "item2"]
# 对象类型
people:
	# name必须填
  name: jack
# 规定为数字字母下划线
alphanumeric: "abc123_"
# 枚举-单选
single_choice: "option1"
# 枚举-多选
multi_choice: ["option1", "option2"]

2.2.1 JsonSchema

{
  "type": "object",
  "properties": {
    "number_field": {
      "type": "integer",
      "minimum": 0,
      "maximum": 50
    },
    "string_field": {
      "type": "string"
    },
    "array_field": {
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "people": {
      "type": "object",
      "properties": {
        "name": {
          "type": "string"
        }
      },
      "required": ["name"]
    },
    "alphanumeric": {
      "type": "string",
      "pattern": "^[a-zA-Z0-9_]+$"
    },
    "single_choice": {
      "type": "string",
      "enum": ["option1", "option2", "option3"]
    },
    "multi_choice": {
      "type": "array",
      "items": {
        "type": "string",
        "enum": ["option1", "option2", "option3"]
      }
    }
  },
  "required": ["alphanumeric", "single_choice", "multi_choice", "array_field", "number_field", "string_field", "people"],
  "errorMessage": {
    "required": "缺少必填字段:''{0}/{1}''",
    "enum": "''{0}''的值必须是:''{1}''中的一个",
    "type": "''{0}''的类型必须是: ''{2}'',但你提供的是''{1}''"
  }
}

2.2.2 YamlSchema

type: object
  properties:
    alphanumeric:
      type: string
      pattern: "^[a-zA-Z0-9_]+$"
      errorMessage:
        pattern: "alphanumeric 字段只允许包含字母、数字和下划线"
    single_choice:
      type: string
      enum: ["option1", "option2", "option3"]
    multi_choice:
      type: array
      items:
        type: string
        enum: ["option1", "option2", "option3"]
        errorMessage:
          enum: "multi_choice 数组的每一项必须是 option1, option2, option3 其中之一"
      errorMessage:
        type: "multi_choice 字段必须是一个数组"
    array_field:
      type: array
      items:
        type: string
      errorMessage: "array_field 字段必须是一个字符串数组"
    number_field:
      type: integer
      minimum: 0
      maximum: 50
      errorMessage: "number_field 字段的值必须是 0 到 50 之间的整数"
    string_field:
      type: string
      errorMessage: "string_field 字段必须是一个字符串"
    people:
      type: object
      properties:
        name:
          type: string
      required:
        - name
      errorMessage:
        required:
          name: "people.name 字段是必填项"
  required:
    - alphanumeric
    - single_choice
    - multi_choice
    - array_field
    - number_field
    - string_field
    - people
  errorMessage:
    "required": "缺少必填字段:''{0}/{1}''"
    "enum": "''{0}''的值必须是:''{1}''中的一个"
    "type": "''{0}''的类型必须是: ''{2}'',但你提供的是''{1}''"

3. 如何使用Java项目进行yaml文件的验证

3.1 导入Maven包

<dependency>
    <groupId>com.networknt</groupId>
    <artifactId>json-schema-validator</artifactId>
  	<!--注意替换成你的版本/最新版本-->
    <version>1.4.2</version>
</dependency>

<dependency>
    <groupId>com.fasterxml.jackson</groupId>
    <artifactId>jackson-bom</artifactId>
	  <!--注意替换成你的版本/最新版本-->
    <version>2.12.5</version>
</dependency>

3.2 准备测试数据

data.yml

valid_data:
  alphanumeric: "abc123_"
  single_choice: "option1"
  multi_choice: ["option1", "option2"]
  array_field: ["item1", "item2"]
  number_field: 25
  string_field: "some string"

invalid_data:
  alphanumeric: "abc 123!"  # 包含无效字符
  single_choice: "invalid_option"  # 不在选项列表中
  multi_choice: ["option1", 123]  # 包含无效类型
  array_field: "not an array"  # 不是数组
  number_field: 55  # 超出范围
  string_field: 123  # 不是字符串
  people:
    age: 25  # 不是对象
    address:
      city: "London"

3.3 编写代码

3.3.1 使用JsonSchema

3.3.1.2 编写schema

schema.json

{
  "type": "object",
  "properties": {
    "alphanumeric": {
      "type": "string",
      "pattern": "^[a-zA-Z0-9_]+$"
    },
    "single_choice": {
      "type": "string",
      "enum": ["option1", "option2", "option3"]
    },
    "people": {
      "type": "object",
      "properties": {
        "name": {
          "type": "string"
        },
        "address": {
          "type": "object",
          "province": {
            "type": "string"
          },
          "required": ["province"]
        }
      },
      "required": ["name", "address"]
    },
    "multi_choice": {
      "type": "array",
      "items": {
        "type": "string",
        "enum": ["option1", "option2", "option3"]
      }
    },
    "array_field": {
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "number_field": {
      "type": "integer",
      "minimum": 0,
      "maximum": 50
    },
    "string_field": {
      "type": "string"
    }
  },
  "required": ["alphanumeric", "single_choice", "multi_choice", "array_field", "number_field", "string_field", "people"],
  "errorMessage": {
    "required": "缺少必填字段:''{0}/{1}''",
    "enum": "''{0}''的值必须是:''{1}''中的一个",
    "type": "''{0}''的类型必须是: ''{2}'',但你提供的是''{1}''"
  }
}
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import com.networknt.schema.JsonSchema;
import com.networknt.schema.JsonSchemaFactory;
import com.networknt.schema.SchemaValidatorsConfig;
import com.networknt.schema.SpecVersion;
import com.networknt.schema.ValidationMessage;
import org.springframework.core.io.ClassPathResource;

import java.io.IOException;
import java.util.Set;

public class YamlValidator {

    public static void main(String[] args) throws IOException {
        // 读取和解析 YAML数据 文件
        JsonNode yamlNode = new ObjectMapper(new YAMLFactory())
                .readTree(new ClassPathResource("data.yml").getInputStream());

        // 读取和解析 JSON Schema 文件
        JsonSchema schema = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V202012)
                .getSchema(new ClassPathResource("schema.json").getInputStream(), SchemaValidatorsConfig.builder()
                        .errorMessageKeyword("errorMessage")
                        .build());
       // 校验其中一个数据,你也可以校验valid_data
      // 另外,如果你的data文件,是一整个yaml,不区分分数据块,那么你无需yamlNode.get("..."),直接使用yamlNode自身即可
	 		 Set<ValidationMessage> validDataErrors = schema.validate(yamlNode.get("invalid_data"));
       if (validDataErrors.isEmpty()) {
            System.out.println(dataName + " 通过校验");
        } else {
            System.out.println(dataName + " 校验错误:");
            validDataErrors.forEach(error -> System.out.println(error.getMessage()));
        }
    }
}

3.3.1 使用YamlSchema

3.3.1.2 编写schema

schema.yaml

type: object
  properties:
    alphanumeric:
      type: string
      pattern: "^[a-zA-Z0-9_]+$"
      errorMessage:
        pattern: "alphanumeric 字段只允许包含字母、数字和下划线"
    single_choice:
      type: string
      enum: ["option1", "option2", "option3"]
    multi_choice:
      type: array
      items:
        type: string
        enum: ["option1", "option2", "option3"]
        errorMessage:
          enum: "multi_choice 数组的每一项必须是 option1, option2, option3 其中之一"
      errorMessage:
        type: "multi_choice 字段必须是一个数组"
    array_field:
      type: array
      items:
        type: string
      errorMessage: "array_field 字段必须是一个字符串数组"
    number_field:
      type: integer
      minimum: 0
      maximum: 50
      errorMessage: "number_field 字段的值必须是 0 到 50 之间的整数"
    string_field:
      type: string
      errorMessage: "string_field 字段必须是一个字符串"
    people:
      type: object
      properties:
        name:
          type: string
      required:
        - name
      errorMessage:
        required:
          name: "people.name 字段是必填项"
  required:
    - alphanumeric
    - single_choice
    - multi_choice
    - array_field
    - number_field
    - string_field
    - people
  errorMessage:
    "required": "缺少必填字段:''{0}/{1}''"
    "enum": "''{0}''的值必须是:''{1}''中的一个"
    "type": "''{0}''的类型必须是: ''{2}'',但你提供的是''{1}''"
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import com.networknt.schema.InputFormat;
import com.networknt.schema.JsonSchema;
import com.networknt.schema.JsonSchemaFactory;
import com.networknt.schema.SchemaValidatorsConfig;
import com.networknt.schema.SpecVersion;
import com.networknt.schema.ValidationMessage;
import com.networknt.schema.serialization.JsonNodeReader;

import java.util.Set;

public class YamlTest2 {

    public static void main(String[] args) {
      	// schema文件内容
        String schemaData2 = "...上述的schema文件内容";
        // 数据内容,Java17的多行字符串写法
        String inputData = """
                alphanumeric: '1)'
                single_choice: option5
                multi_choice: 1
                people:
                  age: 10
                """;
        // 构建yamlSchema数据读取器
        JsonNodeReader jsonNodeReader = JsonNodeReader.builder().yamlMapper(new ObjectMapper(new YAMLFactory())).build();
        JsonSchemaFactory factory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V202012,
                builder -> builder.jsonNodeReader(jsonNodeReader).build());
        SchemaValidatorsConfig config = SchemaValidatorsConfig.builder()
                .errorMessageKeyword("errorMessage")
                .build();
	    
	    // 读取schema
        JsonSchema schema = factory.getSchema(schemaData2, InputFormat.YAML, config);
        // 校验数据
        Set<ValidationMessage> validDataErrors = schema.validate(inputData, InputFormat.YAML);
        // 打印校验失败的信息
        validDataErrors.forEach(error -> System.out.println(error.getMessage()));
    }
}

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值