Json schema校验json字符串(networknt/json-schema-validator库)

news2025/5/20 18:58:08

学习链接

json-schema官网 - 英文

jsonschemavalidator 可在线校验网站

networknt的json-schema-validator github地址

networknt的json-schema-validator 个人gitee地址 - 里面有md文档说明和代码示例

JSON Schema 入门指南:如何定义和验证 JSON 数据结构

JSON Schema入门 - 比较详细

文章目录

  • 学习链接
  • 1、JSON Schema 入门指南
    • 1、引言
    • 2、什么是 JSON Schema?
    • 3、JSON Schema 的基本结构
      • 3.1 基本关键字
      • 3.2 对象属性
      • 3.3 数组元素
      • 3.4 字符串约束
      • 3.5 数值约束
    • 4、示例:定义一个简单的 JSON Schema
  • 2、java示例
    • 源码结构图
    • 1、普通属性校验示例
      • pom.xml
      • SchemaValidatorExample
      • ValidationResult
      • schema.json
      • json-validator-message.properties
      • json-validator-message_zh_CN.properties
      • ResourceBundleTest
    • 2、嵌套属性校验示例
      • SchemaValidatorExample
      • schema.json
      • json-validator-message.properties
      • json-validator-message_zh_CN.properties
    • 3、$ref 引用
      • 1、内部引用
        • resources/user-schema.json
        • JsonSchemaValidatorDemo
      • 2、外部文件引用
        • JsonSchemaValidatorDemo
        • CommonSchemaLoader
        • ==resources/user-schema.json==
        • resources/common/address-schema.json
        • resources/user-schema.json
    • 4、format校验
      • 1、内置format校验示例
        • JsonSchemaValidatorDemo
        • format-schema.json
      • 2、覆盖内置的format校验逻辑
        • JsonSchemaValidatorDemo
        • resources/schemas/format-schema.json
      • 3、自定义format校验
        • JsonSchemaValidatorDemo
        • DateTimeFormat
        • json-validator-message.properties
    • 5、自定义Validator校验
      • 示例1
        • CustomTypeCode
        • ValidatorFactory
        • AbsBaseJsonValidator
        • json-validator-message_zh_CN.properties
        • json-validator-message.properties
        • MobilePhonePrefixValidator
        • JsonSchemaValidatorDemo
      • 示例2(推荐)
        • JsonSchemaValidatorDemo2
        • GroovyKeyword
        • resources/schemas/format-schema.json
        • json-validator-message.properties
        • json-validator-message_zh_CN.properties

1、JSON Schema 入门指南

1、引言

在现代的 Web 开发和数据交换中,JSON(JavaScript Object Notation)已经成为了一种非常流行的数据格式。它轻量、易读、易于解析,广泛应用于 API 通信、配置文件、数据存储等场景。然而,随着 JSON 数据结构的复杂性增加,如何确保 JSON 数据的有效性和一致性成为了一个挑战。这时,JSON Schema 就派上了用场。

本文将带你入门 JSON Schema,帮助你理解它的基本概念、语法结构,并通过实例演示如何使用 JSON Schema 来定义和验证 JSON 数据结构。

2、什么是 JSON Schema?

JSON Schema 是一种用于描述 JSON 数据结构的规范。它允许你定义 JSON 数据的格式、类型、约束条件等,从而确保 JSON 数据符合预期的结构。通过 JSON Schema,你可以在数据交换、存储或处理之前,对 JSON 数据进行验证,确保其有效性和一致性。

简单来说,JSON Schema 就像是 JSON 数据的“蓝图”或“合同”,它规定了 JSON数据应该长什么样子。

3、JSON Schema 的基本结构

一个 JSON Schema 本身也是一个 JSON 对象。它由一系列关键字(keywords)组成,这些关键字用于定义 JSON 数据的结构和约束条件。

3.1 基本关键字

  • $schema: 指定使用的 JSON Schema 版本。例如,"$schema": "http://json-schema.org/draft-07/schema#" 表示使用 Draft 7 版本的 JSON Schema。
  • $id: 为 Schema 定义一个唯一的标识符,通常是一个 URL。
  • titledescription: 分别为 Schema 提供标题和描述信息,便于理解和维护。
  • type: 定义 JSON 数据的类型。常见的类型有 object、array、string、number、integer、booleannull

3.2 对象属性

  • properties: 定义对象中的各个属性及其对应的 Schema。
  • required: 指定哪些属性是必须的。
  • additionalProperties: 控制是否允许对象包含未在 properties 中定义的额外属性。

3.3 数组元素

  • items: 定义数组中每个元素的 Schema。
  • minItemsmaxItems: 分别指定数组的最小和最大长度。
  • uniqueItems: 指定数组中的元素是否必须唯一。

3.4 字符串约束

  • minLength 和 maxLength: 分别指定字符串的最小和最大长度。
  • pattern: 使用正则表达式约束字符串的格式。
  • format: 指定字符串的格式,如 email、date-time 等。

3.5 数值约束

  • minimum 和 maximum: 分别指定数值的最小和最大值。
  • exclusiveMinimumexclusiveMaximum: 指定数值是否排除最小值和最大值。
  • multipleOf: 指定数值必须是某个数的倍数。

4、示例:定义一个简单的 JSON Schema

假设我们要定义一个表示用户信息的 JSON 数据结构,要求如下:

  • 用户对象必须包含 idnameemail 属性。
  • id 必须是整数。
  • name 必须是字符串,且长度在 1 到 50 之间。
  • email 必须是有效的电子邮件地址。
  • 用户对象可以包含可选的 age 属性,且必须是正整数。

对应的 JSON Schema 可以这样定义:

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "https://example.com/user.schema.json",
  "title": "User",
  "description": "A user object",
  "type": "object",
  "properties": {
    "id": {
      "type": "integer"
    },
    "name": {
      "type": "string",
      "minLength": 1,
      "maxLength": 50
    },
    "email": {
      "type": "string",
      "format": "email"
    },
    "age": {
      "type": "integer",
      "minimum": 0,
      "exclusiveMinimum": true
    }
  },
  "required": ["id", "name", "email"],
  "additionalProperties": false
}

解释

  • $schema$id 分别指定了 Schema 的版本和唯一标识符。
  • type 指定了 JSON 数据的类型为 object
  • properties 定义了对象的各个属性及其约束条件。
  • required 指定了 idnameemail 是必须的属性。
  • additionalProperties 设置为 false,表示不允许对象包含未定义的属性。

2、java示例

源码结构图

在这里插入图片描述
在这里插入图片描述

1、普通属性校验示例

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.4</version>
    </parent>

    <modelVersion>4.0.0</modelVersion>

    <groupId>com.zzhua</groupId>
    <artifactId>demo-metrics</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <dependency>
            <groupId>io.micrometer</groupId>
            <artifactId>micrometer-registry-prometheus</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <!-- https://mvnrepository.com/artifact/com.networknt/json-schema-validator -->
        <dependency>
            <groupId>com.networknt</groupId>
            <artifactId>json-schema-validator</artifactId>
            <version>1.5.6</version>
        </dependency>

        <dependency>
            <!-- Used to validate ECMA 262 regular expressions -->
            <!-- Approximately 50 MB in dependencies -->
            <!-- GraalJSRegularExpressionFactory -->
            <groupId>org.graalvm.js</groupId>
            <artifactId>js</artifactId>
            <version>21.3.10</version>
        </dependency>

        <dependency>
            <!-- Used to validate ECMA 262 regular expressions -->
            <!-- Approximately 2 MB in dependencies -->
            <!-- JoniRegularExpressionFactory -->
            <groupId>org.jruby.joni</groupId>
            <artifactId>joni</artifactId>
            <version>2.2.1</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

SchemaValidatorExample

import com.fasterxml.jackson.databind.ObjectMapper;
import com.networknt.schema.*;
import com.networknt.schema.i18n.ResourceBundleMessageSource;
import com.zzhua.util.ValidationResult;
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.StringUtils;

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

public class SchemaValidatorExample {
    public static void main(String[] args) throws IOException {

        JsonSchemaFactory schemaFactory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7);

        JsonSchema schema = schemaFactory.getSchema(
                new ClassPathResource("schema/test04/schema.json").getInputStream(),
                SchemaValidatorsConfig
                        .builder()
                        // 可以去掉JsonNodePath instanceLocation前面的 / 斜线
                        .pathType(PathType.URI_REFERENCE)
                        // 快速失败
                        // .failFast(true)
                        // 自定义消息源(按顺序找指定的文件名。可支持i18n国际化。错误提示中的占位符的值可以看XxxValidator往arguments中设置了哪些值。)
                        .messageSource(new ResourceBundleMessageSource(/*"validate/json-validator-message",*/"jsv-messages"))
                        // 自定义消息错误提示属性
                        // (虽然SchemaValidatorsConfig#errorMessageKeyword默认值就是message,
                        //   但是builder中该属性默认是null,所以这里如果不主动设置的话,就会是null)
                        .errorMessageKeyword("message")
                        .build()
        );

        ObjectMapper mapper = new ObjectMapper();
        String jsonStr = """
                {
                    "age": 5,
                    "sex": true,
                    "email": "haloC",
                    "motto": "人心中的成见是一座大山,任你怎么努力使劲,也休想搬动",
                    "hobbies": ["java", 666, "spring" ]
                }
                """;

        Set<ValidationMessage> validationMessages = schema.validate(mapper.readTree(jsonStr));
        List<ValidationResult> results = new ArrayList<>();
        for (ValidationMessage validationMessage : validationMessages) {
            results.add(ValidationResult.builder()
                    .type(validationMessage.getType())
                    .errMsg(validationMessage.getMessage())
                    .propertyPath(deducePropertyPath(validationMessage))
                    .build());
        }

        for (ValidationResult validationResult : results) {
            System.out.println("-------------------");
            System.out.println("校验的关键字: "   + validationResult.getType());
            System.out.println("校验的属性: "     + validationResult.getPropertyPath());
            System.out.println("校验的错误提示: "  + validationResult.getErrMsg());
        }
    }

    private static String deducePropertyPath(ValidationMessage validationMessage) {
        // 推断属性名
        String property = validationMessage.getProperty();
        if (StringUtils.hasText(property)) {
            return property;
        }

        JsonNodePath instanceLocation = validationMessage.getInstanceLocation();
        if (instanceLocation != null) {
            return instanceLocation.toString();
        }

        JsonNodePath evaluationPath = validationMessage.getEvaluationPath();
        if (evaluationPath != null) {
            return evaluationPath.toString();
        }

        return "unknown";
    }
}


ValidationResult

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class ValidationResult {
    private String type;
    private String propertyPath;
    private String errMsg;
}

schema.json

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "message": {
        "type": "name的类型不对, 这里的优先级高"
      }
    },
    "age": {
      "type": "integer",
      "minimum": 10,
      "maximum": 120,
      "message": {
        "minimum": "年龄不能小于10岁哦",
        "maximum": "年龄不能大于120岁哦"
      }
    },
    "sex": {
      "type": "string",
      "enum": ["male", "female"],
      "message": {
        "enum": "这是个枚举哦, 它的取值有: {1}, 这里的优先级比外面优先级要高"
      }
    },
    "email": {
      "pattern": "^\\w{3,8}@\\w{3,8}\\.com$"
    },
    "motto": {
      "type": "string",
      "maxLength": 20,
      "minLength": 5,
      "message": {
        "minLength": "这个字数太短啦, 最少5个字"
      }
    },
    "hobbies": {
      "type": "array",
      "items": {
        "type": "string"
      }
    }
  },
  "required": ["name","age"],
  "message": {
    "required": {
      "name": "没有提供名字name",
      "age": "没有提供age年龄"
    },
    "type": "属性 {0} 设置了 {1} 类型不正确,而应当为 {2} ",
    "enum": "这是个枚举值, 取值有: {1}, 这里的优先级较低",
    "pattern": "不符合这个正则表达式咯",
    "maxLength": "这个字数太长啦, 最长20个字,看看哪里的生效~这里的不生效,上面如果要写就要写全了"
  }
}

json-validator-message.properties

maxLength = ~~~{0}: zzhua~~~长度不得超过 {1} 个字符~~~

json-validator-message_zh_CN.properties

maxLength = ~~~{0}: zzhua-中文~~~长度不得超过 {1} 个字符~~~

ResourceBundleTest

用于加载属性配置文件,并支持i18n国际化

import java.util.Locale;
import java.util.ResourceBundle;

public class ResourceBundleTest {

    public static void main(String[] args) {

        // 1、ResourceBundle resourceBundle = ResourceBundle.getBundle(baseName, locale)
        // 2、String val = resourceBundle.getString(key)
        // 3、文件名:jsv-messages_zh_CN.properties
        // 4、MessageFormat format = new MessageFormat("{0}: 未知属性{1}", locale)
        // 5、String result = format.format(arguements, new StringBuffer(), null)

        // ResourceBundle bundle = ResourceBundle.getBundle("validate/json-validator-message", Locale.ROOT);
        ResourceBundle bundle = ResourceBundle.getBundle("validate/json-validator-message", Locale.CHINA);
        String maxLength = bundle.getString("maxLength");
        System.out.println(maxLength);

    }

}

2、嵌套属性校验示例

SchemaValidatorExample

package com.zzhua.test05;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.networknt.schema.*;
import com.networknt.schema.i18n.ResourceBundleMessageSource;
import com.zzhua.util.ValidationResult;
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.StringUtils;

import javax.xml.validation.SchemaFactory;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

public class SchemaValidatorExample05 {

    public static void main(String[] args) throws IOException {

        JsonSchemaFactory schemaFactory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7);

        JsonSchema schema = schemaFactory.getSchema(
                new ClassPathResource("schema/test05/schema.json").getInputStream(),
                SchemaValidatorsConfig
                        .builder()
                        // 可以去掉JsonNodePath instanceLocation前面的 / 斜线
                        .pathType(PathType.URI_REFERENCE)
                        // 快速失败
                        // .failFast(true)
                        // 自定义消息源(按顺序找指定的文件名。可支持i18n国际化。错误提示中的占位符的值可以看XxxValidator往arguments中设置了哪些值。)
                        .messageSource(new ResourceBundleMessageSource("validate/json-validator-message","jsv-messages"))
                        // 自定义消息错误提示属性
                        // (虽然SchemaValidatorsConfig#errorMessageKeyword默认值就是message,
                        //   但是builder中该属性默认是null,所以这里如果不主动设置的话,就会是null)
                        .errorMessageKeyword("message")
                        .build()
        );

        ObjectMapper mapper = new ObjectMapper();
        String jsonStr = """
                {
                  "user": {
                    "a": "b",
                    "name": 1,
                    "address": {
                      "street": "123 Main St",
                      "city": "Beijing",
                      "coordinates": {
                        "lat": 39.9042,
                        "lng": 116.4074
                      }
                    },
                    "friends": [
                      {
                        "name": "Bob",
                        "hobbies": ["Reading", "Sports"]
                      },
                      {
                        "name": "Charlie",
                        "hobbies": ["Music"]
                      }
                    ]
                  }
                }
                """;

        Set<ValidationMessage> validationMessages = schema.validate(mapper.readTree(jsonStr));
        List<com.zzhua.util.ValidationResult> results = new ArrayList<>();
        for (ValidationMessage validationMessage : validationMessages) {
            results.add(com.zzhua.util.ValidationResult.builder()
                    .type(validationMessage.getType())
                    .errMsg(validationMessage.getMessage())
                    .propertyPath(deducePropertyPath(validationMessage))
                    .build());
        }

        for (ValidationResult validationResult : results) {
            System.out.println("-------------------");
            System.out.println("校验的关键字: "   + validationResult.getType());
            System.out.println("校验的属性: "     + validationResult.getPropertyPath());
            System.out.println("校验的错误提示: "  + validationResult.getErrMsg());
        }


    }

    private static String deducePropertyPath(ValidationMessage validationMessage) {
        // 推断属性名
        String property = validationMessage.getProperty();
        if (StringUtils.hasText(property)) {
            return property;
        }

        JsonNodePath instanceLocation = validationMessage.getInstanceLocation();
        if (instanceLocation != null) {
            return instanceLocation.toString();
        }

        JsonNodePath evaluationPath = validationMessage.getEvaluationPath();
        if (evaluationPath != null) {
            return evaluationPath.toString();
        }

        return "unknown";
    }

}

schema.json

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "required": ["user"],
  "properties": {
    "user": {
      "type": "object",
      "required": ["name", "age", "address"],
      "additionalProperties": false,
      "message": {
        "required": "{0}中缺少属性{1}",
        "additionalProperties": "{0}中不允许存在额外属性{1}"
      },
      "properties": {
        "name": { "type": "string" },
        "age": { "type": "number", "minimum": 0 },
        "address": {
          "type": "object",
          "required": ["street", "city"],
          "properties": {
            "street": { "type": "string" },
            "city": { "type": "string" },
            "coordinates": {
              "type": "object",
              "required": ["lat", "lng"],
              "properties": {
                "lat": { "type": "number" },
                "lng": { "type": "number" }
              },
              "additionalProperties": false
            }
          }
        },
        "friends": {
          "type": "array",
          "items": {
            "type": "object",
            "required": ["name"],
            "properties": {
              "name": { "type": "string" },
              "hobbies": {
                "type": "array",
                "items": { "type": "string" },
                "minItems": 1
              }
            },
            "additionalProperties": false
          }
        }
      }
    }
  }
}

json-validator-message.properties

type = 类型不太对哦{1}-{2}

json-validator-message_zh_CN.properties

type = 中文-类型不太对哦{1}-{2}

3、$ref 引用

1、内部引用

resources/user-schema.json
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "/schemas/user-schema.json",
  "definitions": {
    "address": {
      "type": "object",
      "properties": {
        "street_address": {
          "type": "string"
        },
        "city": {
          "type": "string"
        },
        "state": {
          "type": "string"
        }
      },
      "required": [
        "street_address",
        "city",
        "state"
      ]
    }
  },
  "type": "object",
  "properties": {
    "billing_address": {
      "$ref": "#/definitions/address"
    },
    "shipping_address": {
      "$ref": "#/definitions/address"
    }
  }
}
JsonSchemaValidatorDemo
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.networknt.schema.*;
import com.zzhua.util.ValidationResult;
import org.springframework.util.StringUtils;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

public class JsonSchemaValidatorDemo {
    public static void main(String[] args) throws JsonProcessingException {
        // 1. 加载主 Schema
        JsonSchemaFactory schemaFactory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7);
        InputStream schemaStream = JsonSchemaValidatorDemo.class.getResourceAsStream("/schemas/user-schema.json");
        JsonSchema schema = schemaFactory.getSchema(
                schemaStream,
                SchemaValidatorsConfig
                        .builder()
                        .pathType(PathType.URI_REFERENCE)
                        .build()
        );

        // 2. 准备待校验的 JSON 数据
        String jsonData = """
                {
                    "billing_address": {
                        "city":"shenzhen",
                        "state": "gd",
                        "street_address": "lsydxq48d"
                    }
                }
                """;

        // 3. 执行校验
        Set<ValidationMessage> validationMessages = schema.validate(new ObjectMapper().readTree(jsonData));
        List<com.zzhua.util.ValidationResult> results = new ArrayList<>();
        for (ValidationMessage validationMessage : validationMessages) {
            results.add(com.zzhua.util.ValidationResult.builder()
                    .type(validationMessage.getType())
                    .errMsg(validationMessage.getMessage())
                    .propertyPath(deducePropertyPath(validationMessage))
                    .build());
        }

        for (ValidationResult validationResult : results) {
            System.out.println("-------------------");
            System.out.println("校验的关键字: " + validationResult.getType());
            System.out.println("校验的属性: " + validationResult.getPropertyPath());
            System.out.println("校验的错误提示: " + validationResult.getErrMsg());
        }

    }

    private static String deducePropertyPath(ValidationMessage validationMessage) {
        // 推断属性名
        String property = validationMessage.getProperty();
        if (StringUtils.hasText(property)) {
            return property;
        }

        JsonNodePath instanceLocation = validationMessage.getInstanceLocation();
        if (instanceLocation != null) {
            return instanceLocation.toString();
        }

        JsonNodePath evaluationPath = validationMessage.getEvaluationPath();
        if (evaluationPath != null) {
            return evaluationPath.toString();
        }

        return "unknown";
    }
}

2、外部文件引用

JsonSchemaValidatorDemo
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.networknt.schema.*;
import com.networknt.schema.resource.PrefixSchemaMapper;
import com.zzhua.util.ValidationResult;
import org.springframework.util.StringUtils;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

public class JsonSchemaValidatorDemo {
    public static void main(String[] args) throws JsonProcessingException {

        SchemaValidatorsConfig config = SchemaValidatorsConfig
                .builder()
                .pathType(PathType.URI_REFERENCE)
                .errorMessageKeyword("message")
                .build();

        JsonSchemaFactory schemaFactory = JsonSchemaFactory.getInstance(
                SpecVersion.VersionFlag.V7,
                builder -> {
                    builder.schemaMappers(schemaMapperBuilder -> {
                        schemaMapperBuilder.add(new PrefixSchemaMapper("http://www.example.com", ""));
                    });
                    builder.schemaLoaders(schemaLoaderBuilder -> {
                        schemaLoaderBuilder.add(new CommonSchemaLoader());
                    });
                }
        );

        // 1. 加载主 Schema
        InputStream schemaStream = JsonSchemaValidatorDemo.class.getResourceAsStream("/schemas/user-schema.json");

        JsonSchema schema = schemaFactory.getSchema(schemaStream, config);

        // 2. 准备待校验的 JSON 数据
        String jsonData = """
                {
                    "billing_address":{
                        "state": 1
                    },
                    "customer_name": "ab",
                    "enum_no_use_name": "zzhua",
                    "all_can_use_name": "zz"
                }
                """;

        // 3. 执行校验
        Set<ValidationMessage> validationMessages = schema.validate(new ObjectMapper().readTree(jsonData));
        List<com.zzhua.util.ValidationResult> results = new ArrayList<>();
        for (ValidationMessage validationMessage : validationMessages) {
            results.add(com.zzhua.util.ValidationResult.builder()
                    .type(validationMessage.getType())
                    .errMsg(validationMessage.getMessage())
                    .propertyPath(deducePropertyPath(validationMessage))
                    .build());
        }

        for (ValidationResult validationResult : results) {
            System.out.println("-------------------");
            System.out.println("校验的关键字: " + validationResult.getType());
            System.out.println("校验的属性: " + validationResult.getPropertyPath());
            System.out.println("校验的错误提示: " + validationResult.getErrMsg());
        }

    }

    private static String deducePropertyPath(ValidationMessage validationMessage) {
        // 推断属性名
        String property = validationMessage.getProperty();
        if (StringUtils.hasText(property)) {
            return property;
        }

        JsonNodePath instanceLocation = validationMessage.getInstanceLocation();
        if (instanceLocation != null) {
            return instanceLocation.toString();
        }

        JsonNodePath evaluationPath = validationMessage.getEvaluationPath();
        if (evaluationPath != null) {
            return evaluationPath.toString();
        }

        return "unknown";
    }
}
CommonSchemaLoader

参照 ClasspathSchemaLoader

import com.networknt.schema.AbsoluteIri;
import com.networknt.schema.resource.InputStreamSource;
import com.networknt.schema.resource.SchemaLoader;

import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.function.Supplier;

public class CommonSchemaLoader implements SchemaLoader {

    private final Supplier<ClassLoader> classLoaderSource;

    /**
     * Constructor.
     */
    public CommonSchemaLoader() {
        this(CommonSchemaLoader::getClassLoader);
    }

    public CommonSchemaLoader(Supplier<ClassLoader> classLoaderSource) {
        this.classLoaderSource = classLoaderSource;
    }

    @Override
    public InputStreamSource getSchema(AbsoluteIri absoluteIri) {
        String iri = absoluteIri != null ? absoluteIri.toString() : "";
        String name = null;
        if (iri.startsWith("/schemas/common")) {
            name = iri.substring(1);
        }
        if (name != null) {
            ClassLoader classLoader = this.classLoaderSource.get();
            String resource = name;
            return () -> {
                InputStream result = classLoader.getResourceAsStream(resource);
                if (result == null) {
                    result = classLoader.getResourceAsStream(resource.substring(1));
                }
                if (result == null) {
                    throw new FileNotFoundException(iri);
                }
                return result;
            };
        }
        return null;
    }

    protected static ClassLoader getClassLoader() {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        if (classLoader == null) {
            classLoader = SchemaLoader.class.getClassLoader();
        }
        return classLoader;
    }
}

resources/user-schema.json
  • 当在一个模式对象中使用$ref时,其他属性会被忽略,因为$ref会完全引用外部模式,取代当前对象。这意味着如果用户在同一个对象里同时使用$ref和其他属性,比如description或type,这些额外的属性可能会被验证器忽略,导致不符合预期的结果。

    • 更常用的解决方案是使用allOf来组合引用和本地属性。例如,使用allOf数组,其中一个是$ref引用外部模式,另一个是包含本地属性的对象。这样可以将外部模式和本地属性合并,确保所有约束都被应用。
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "classpath:/schemas/address-schema.json",
  "type": "object",
  "definitions": {
    "address": {
      "type": "object",
      "properties": {
        "street_address": {
          "type": "string"
        },
        "city": {
          "type": "string"
        },
        "state": {
          "type": "string"
        }
      },
      "required": [
        "street_address",
        "city",
        "state"
      ]
    }
  },
  "properties": {
    "billing_address": {
      "$ref": "http://www.example.com/schemas/common/address-schema.json#/properties/address"
    },
    "customer_name": {
      "$ref": "http://www.example.com/schemas/common/nickname-schema.json"
    },
    "enum_no_use_name": {
      "$ref": "http://www.example.com/schemas/common/nickname-schema.json",
      "enum": ["mike","jerry"]
    },
    "all_can_use_name": {
      "allOf": [
        {"$ref": "http://www.example.com/schemas/common/nickname-schema.json"},
        {
          "enum": ["mike","jerryyyyyyyyyyyyyyyy"],
          "message": {"enum": "all_can_use_name枚举值会和$ref引入的内容合并吗?会合并!"}
        }

      ]
    },
    "shipping_address": {
      "$ref": "#/definitions/address"
    }
  },
  "required": ["customer_name"]
}
resources/common/address-schema.json
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "/schemas/nickname-schema.json",
  "type": "string",
  "minLength": 3,
  "maxLength": 8,
  "message": {
    "minLength": "~minLength~昵称长度必须在3-8个字符之间",
    "maxLength": "~maxLength~昵称长度必须在3-8个字符之间"
  }
}
resources/user-schema.json
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "classpath:/schemas/address-schema.json",
  "type": "object",
  "definitions": {
    "address": {
      "type": "object",
      "properties": {
        "street_address": {
          "type": "string"
        },
        "city": {
          "type": "string"
        },
        "state": {
          "type": "string"
        }
      },
      "required": [
        "street_address",
        "city",
        "state"
      ]
    }
  },
  "properties": {
    "billing_address": {
      "$ref": "http://www.example.com/schemas/common/address-schema.json#/properties/address"
    },
    "customer_name": {
      "$ref": "http://www.example.com/schemas/common/nickname-schema.json"
    },
    "shipping_address": {
      "$ref": "#/definitions/address"
    }
  },
  "required": ["customer_name"]
}

4、format校验

1、内置format校验示例

JsonSchemaValidatorDemo
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.networknt.schema.*;
import com.networknt.schema.resource.PrefixSchemaMapper;
import com.zzhua.util.ValidationResult;
import org.springframework.util.StringUtils;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

public class JsonSchemaValidatorDemo {
    public static void main(String[] args) throws JsonProcessingException {

        SchemaValidatorsConfig config = SchemaValidatorsConfig
                .builder()
                .pathType(PathType.URI_REFERENCE)
                .errorMessageKeyword("message")
                .failFast(false)
                // 如果不开启format校验,那么format会只被认为是个注释,而不会去作校验
                .formatAssertionsEnabled(true)
                .build();



        JsonSchemaFactory schemaFactory = JsonSchemaFactory.getInstance(
                SpecVersion.VersionFlag.V7
        );

        // 1. 加载主 Schema
        InputStream schemaStream = JsonSchemaValidatorDemo.class.getResourceAsStream("/schemas/format-schema.json");

        JsonSchema schema = schemaFactory.getSchema(schemaStream, config);

        // 2. 准备待校验的 JSON 数据
        String jsonData = """
                {
                    "mailbox": "111.com"
                }
                """;

        // 3. 执行校验
        Set<ValidationMessage> validationMessages = schema.validate(new ObjectMapper().readTree(jsonData));
        List<com.zzhua.util.ValidationResult> results = new ArrayList<>();
        for (ValidationMessage validationMessage : validationMessages) {
            results.add(com.zzhua.util.ValidationResult.builder()
                    .type(validationMessage.getType())
                    .errMsg(validationMessage.getMessage())
                    .propertyPath(deducePropertyPath(validationMessage))
                    .build());
        }

        for (ValidationResult validationResult : results) {
            System.out.println("-------------------");
            System.out.println("校验的关键字: " + validationResult.getType());
            System.out.println("校验的属性: " + validationResult.getPropertyPath());
            System.out.println("校验的错误提示: " + validationResult.getErrMsg());
        }

    }

    private static String deducePropertyPath(ValidationMessage validationMessage) {
        // 推断属性名
        String property = validationMessage.getProperty();
        if (StringUtils.hasText(property)) {
            return property;
        }

        JsonNodePath instanceLocation = validationMessage.getInstanceLocation();
        if (instanceLocation != null) {
            return instanceLocation.toString();
        }

        JsonNodePath evaluationPath = validationMessage.getEvaluationPath();
        if (evaluationPath != null) {
            return evaluationPath.toString();
        }

        return "unknown";
    }
}
format-schema.json
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "classpath:/schemas/address-schema.json",
  "type": "object",
  "properties": {
    "mailbox": {
      "type": "string",
      "format": "email",
      "message": {
        "format": "邮箱格式不正确的哦"
      }
    }
  },
  "required": ["mailbox"]
}

2、覆盖内置的format校验逻辑

如果内置的format校验逻辑不能满足需求,可以使用自己的覆盖掉内置的逻辑

JsonSchemaValidatorDemo
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.networknt.schema.*;
import com.networknt.schema.format.PatternFormat;
import com.networknt.schema.i18n.ResourceBundleMessageSource;
import com.networknt.schema.resource.PrefixSchemaMapper;
import com.zzhua.util.ValidationResult;
import org.springframework.util.StringUtils;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;

public class JsonSchemaValidatorDemo {
    public static void main(String[] args) throws JsonProcessingException {

        // 这段代码参照 Version7 这个类的代码
        JsonMetaSchema jsonMetaSchemaV7 = JsonMetaSchema.builder(SchemaId.V7)
                .specification(SpecVersion.VersionFlag.V7)
                .idKeyword("$id")
                .formats(Formats.DEFAULT)
                // 直接就覆盖了内置的email (的format校验)
                .format(PatternFormat.of("email", "^@@.com$", "format.email"))
                .keywords(ValidatorTypeCode.getKeywords(SpecVersion.VersionFlag.V7))
                .keywords(Arrays.asList(
                        new NonValidationKeyword("$schema"),
                        new NonValidationKeyword("$id"),
                        new AnnotationKeyword("title"),
                        new AnnotationKeyword("description"),
                        new AnnotationKeyword("default"),
                        new NonValidationKeyword("definitions"),
                        new NonValidationKeyword("$comment"),
                        new AnnotationKeyword("examples"),
                        new NonValidationKeyword("then"),
                        new NonValidationKeyword("else"),
                        new NonValidationKeyword("additionalItems")))
                .build();

        JsonSchemaFactory jsonSchemaFactory = new JsonSchemaFactory.Builder()
                .defaultMetaSchemaIri(jsonMetaSchemaV7.getIri())
                .metaSchema(jsonMetaSchemaV7)
                .build();

        InputStream schemaStream = JsonSchemaValidatorDemo.class.getResourceAsStream("/schemas/format-schema.json");

        SchemaValidatorsConfig config = SchemaValidatorsConfig
                .builder()
                .failFast(false)
                .pathType(PathType.URI_REFERENCE)
                .messageSource(new ResourceBundleMessageSource("json-validator-message", "jsv-messages"))
                .build();

        JsonSchema schema = jsonSchemaFactory.getSchema(schemaStream, config);

        // 2. 准备待校验的 JSON 数据
        String jsonData = """
                {
                    "mailbox": "@@Acom"
                }
                """;

        // 3. 执行校验
        Set<ValidationMessage> validationMessages = schema.validate(new ObjectMapper().readTree(jsonData));
        List<com.zzhua.util.ValidationResult> results = new ArrayList<>();
        for (ValidationMessage validationMessage : validationMessages) {
            results.add(com.zzhua.util.ValidationResult.builder()
                    .type(validationMessage.getType())
                    .errMsg(validationMessage.getMessage())
                    .propertyPath(deducePropertyPath(validationMessage))
                    .build());
        }

        for (ValidationResult validationResult : results) {
            System.out.println("-------------------");
            System.out.println("校验的关键字: " + validationResult.getType());
            System.out.println("校验的属性: " + validationResult.getPropertyPath());
            System.out.println("校验的错误提示: " + validationResult.getErrMsg());
        }

    }

    private static String deducePropertyPath(ValidationMessage validationMessage) {
        // 推断属性名
        String property = validationMessage.getProperty();
        if (StringUtils.hasText(property)) {
            return property;
        }

        JsonNodePath instanceLocation = validationMessage.getInstanceLocation();
        if (instanceLocation != null) {
            return instanceLocation.toString();
        }

        JsonNodePath evaluationPath = validationMessage.getEvaluationPath();
        if (evaluationPath != null) {
            return evaluationPath.toString();
        }

        return "unknown";
    }
}
resources/schemas/format-schema.json
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "classpath:/schemas/address-schema.json",
  "type": "object",
  "properties": {
    "mailbox": {
      "type": "string",
      "format": "email"
    }
  }
}

3、自定义format校验

JsonSchemaValidatorDemo
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.networknt.schema.*;
import com.networknt.schema.i18n.ResourceBundleMessageSource;
import com.networknt.schema.resource.PrefixSchemaMapper;
import com.zzhua.util.ValidationResult;
import org.springframework.util.StringUtils;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

public class JsonSchemaValidatorDemo {
    public static void main(String[] args) throws JsonProcessingException {

        SchemaValidatorsConfig config = SchemaValidatorsConfig
                .builder()
                .pathType(PathType.URI_REFERENCE)
                .errorMessageKeyword("message")
                .failFast(false)
                // 如果不开启format校验,那么format会被认为只是个注释,而不会去作校验
                .formatAssertionsEnabled(true)
                .messageSource(new ResourceBundleMessageSource("validate/json-validator-message","jsv-messages"))
                .build();



        JsonSchemaFactory schemaFactory = JsonSchemaFactory.getInstance(
                SpecVersion.VersionFlag.V7,
                builder -> {
                    builder.schemaMappers(schemaMapperBuilder -> {
                        schemaMapperBuilder.add(new PrefixSchemaMapper("http://www.example.com", ""));
                    });
                    builder.schemaLoaders(schemaLoaderBuilder -> {
                        schemaLoaderBuilder.add(new CommonSchemaLoader());
                    });
                    builder.metaSchemas(jsonMetaSchemaMap -> {
                        // 1、JsonSchemaFactory$Builder 构建 JsonSchemaFactory(用于维护了一堆的JsonMetaSchema)
                        // 2、JsonSchemaVersion 接口 用于提供 JsonMetaSchema 实例,
                        //    该接口下有 Version7、Version4 等版本,
                        //    比如 Version7 提供了 属于当前版本的 JsonMetaSchema 对 draft07 的json-schema校验支持
                        // 3、此处理解的关键代码在 JsonMetaSchema#createKeywordsMap(Map<String, Keyword> kwords, Map<String, Format> formats)
                        // 4、format规则实际运行参照 FormatKeyword
                        JsonMetaSchema jsonMetaSchemaV7 = jsonMetaSchemaMap.get(JsonMetaSchema.getV7().getIri());
                        if (jsonMetaSchemaV7 != null) {
                            JsonMetaSchema jsonMetaSchema = JsonMetaSchema.builder(jsonMetaSchemaV7)
                                    // 自定义format校验
                                    .format(new DateTimeFormat())
                                    .build();
                            jsonMetaSchemaMap.put(JsonMetaSchema.getV7().getIri(), jsonMetaSchema);
                        }
                    });
                }
        );

        // 1. 加载主 Schema
        InputStream schemaStream = JsonSchemaValidatorDemo.class.getResourceAsStream("/schemas/format-schema.json");

        JsonSchema schema = schemaFactory.getSchema(schemaStream, config);

        // 2. 准备待校验的 JSON 数据
        String jsonData = """
                {
                    "mailbox": "12551",
                    "expiryTime": "2023-12:25"
                }
                """;

        // 3. 执行校验
        Set<ValidationMessage> validationMessages = schema.validate(new ObjectMapper().readTree(jsonData));
        List<com.zzhua.util.ValidationResult> results = new ArrayList<>();
        for (ValidationMessage validationMessage : validationMessages) {
            results.add(com.zzhua.util.ValidationResult.builder()
                    .type(validationMessage.getType())
                    .errMsg(validationMessage.getMessage())
                    .propertyPath(deducePropertyPath(validationMessage))
                    .build());
        }

        for (ValidationResult validationResult : results) {
            System.out.println("-------------------");
            System.out.println("校验的关键字: " + validationResult.getType());
            System.out.println("校验的属性: " + validationResult.getPropertyPath());
            System.out.println("校验的错误提示: " + validationResult.getErrMsg());
        }

    }

    private static String deducePropertyPath(ValidationMessage validationMessage) {
        // 推断属性名
        String property = validationMessage.getProperty();
        if (StringUtils.hasText(property)) {
            return property;
        }

        JsonNodePath instanceLocation = validationMessage.getInstanceLocation();
        if (instanceLocation != null) {
            return instanceLocation.toString();
        }

        JsonNodePath evaluationPath = validationMessage.getEvaluationPath();
        if (evaluationPath != null) {
            return evaluationPath.toString();
        }

        return "unknown";
    }
}
DateTimeFormat
import com.networknt.schema.ExecutionContext;
import com.networknt.schema.Format;
import org.springframework.util.StringUtils;

import java.time.format.DateTimeFormatter;

public class DateTimeFormat implements Format {

    /* thread safe */
    public static final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    @Override
    public boolean matches(ExecutionContext executionContext, String value) {
        try {

            if (!StringUtils.hasText(value)) {
                return false;
            }

            dateTimeFormatter.parse(value);

            return true;
        } catch (Exception e) {
            return false;
        }
    }

    @Override
    public String getName() {
        // schema.json中填写format对应的值,如:format: "datetime"
        return "datetime";
    }

    @Override
    public String getMessageKey() {
        // 取错误信息的key,如:"format.datetime"
        return "format.datetime";
    }

}
json-validator-message.properties
format.datetime = {0}:  {1} 时间格式需要符合yyyy-MM-dd HH:mm:ss格式哦

5、自定义Validator校验

示例1

(或者,交给自定义Format校验就行了,用不着自定义Validator校验)

这里模仿 ValidatorTypeCodeEnumsValidatorMaxLengthValidatorBaseJsonValidatorKeywordErrorMessageType 这些原类,代码基本复制这些类,但支持实现自定义Validator校验。

下面检查手机号码是否以指定的前缀开头。

CustomTypeCode
import com.fasterxml.jackson.databind.JsonNode;
import com.networknt.schema.*;

public class CustomTypeCode implements Keyword, ErrorMessageType {

    public static final CustomTypeCode CUSTOM_PHONE = new CustomTypeCode("mobilePhone", "202501", MobilePhonePrefixValidator::new);

    private final String value;
    private final String errorCode;

    private final ValidatorFactory validatorFactory;

    public CustomTypeCode(String value, String errorCode, ValidatorFactory validatorFactory) {
        this.value = value;
        this.errorCode = errorCode;
        this.validatorFactory = validatorFactory;
    }

    @Override
    public String getValue() {
        return value;
    }

    @Override
    public String getErrorCode() {
        return this.errorCode;
    }

    @Override
    public String getErrorCodeValue() {
        return getValue();
    }

    @Override
    public JsonValidator newValidator(SchemaLocation schemaLocation, JsonNodePath evaluationPath, JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext) throws JsonSchemaException, Exception {
        if (this.validatorFactory == null) {
            throw new UnsupportedOperationException("No suitable validator for " + getValue());
        }
        return validatorFactory.newInstance(
                schemaLocation, evaluationPath,
                schemaNode, parentSchema,
                validationContext);
    }
}
ValidatorFactory
@FunctionalInterface
interface ValidatorFactory {
    JsonValidator newInstance(SchemaLocation schemaLocation, JsonNodePath evaluationPath,
                              JsonNode schemaNode, JsonSchema parentSchema,
                              ValidationContext validationContext);
}
AbsBaseJsonValidator

仿照BaseJsonValidator实现

import com.fasterxml.jackson.databind.JsonNode;
import com.networknt.schema.*;
import com.networknt.schema.i18n.DefaultMessageSource;

public abstract class AbsBaseJsonValidator extends ValidationMessageHandler  implements JsonValidator {

    protected final boolean suppressSubSchemaRetrieval;

    protected final JsonNode schemaNode;

    protected final ValidationContext validationContext;


    public AbsBaseJsonValidator(SchemaLocation schemaLocation, JsonNodePath evaluationPath, JsonNode schemaNode,
                             JsonSchema parentSchema, ErrorMessageType errorMessageType, Keyword keyword,
                             ValidationContext validationContext, boolean suppressSubSchemaRetrieval) {
        super(errorMessageType,
                (validationContext != null && validationContext.getConfig() != null)
                        ? validationContext.getConfig().getErrorMessageKeyword()
                        : null,
                (validationContext != null && validationContext.getConfig() != null)
                        ? validationContext.getConfig().getMessageSource()
                        : DefaultMessageSource.getInstance(),
                keyword,
                parentSchema, schemaLocation, evaluationPath);
        this.validationContext = validationContext;
        this.schemaNode = schemaNode;
        this.suppressSubSchemaRetrieval = suppressSubSchemaRetrieval;
    }

    @Override
    public SchemaLocation getSchemaLocation() {
        return this.schemaLocation;
    }

    @Override
    public JsonNodePath getEvaluationPath() {
        return this.evaluationPath;
    }

    @Override
    public String getKeyword() {
        return this.keyword.getValue();
    }

}
json-validator-message_zh_CN.properties
mobilePhone = 属性: {0}, 手机号码不是以前缀 {1} 开头, 实际的值为 {2}
json-validator-message.properties
mobilePhone = 属性: {0}, 手机号码不是以前缀 {1} 开头, 实际的值为 {2}
MobilePhonePrefixValidator
import com.fasterxml.jackson.databind.JsonNode;
import com.networknt.schema.*;
import org.springframework.util.StringUtils;

import java.util.Collections;
import java.util.Set;

public class MobilePhonePrefixValidator extends AbsBaseJsonValidator {

    private static final String KEYWORD = "mobilePhone";

    private String prefix = "";

    public MobilePhonePrefixValidator(SchemaLocation schemaLocation, JsonNodePath evaluationPath,
                                      JsonNode schemaNode, JsonSchema parentSchema,
                                      ValidationContext validationContext) {
        super(schemaLocation,
                evaluationPath,
                schemaNode,
                parentSchema,
                CustomTypeCode.CUSTOM_PHONE,
                CustomTypeCode.CUSTOM_PHONE,
                validationContext,
                false);

        System.out.println("进入MobilePhoneValidatorPrefix构造方法");

        if (schemaNode != null && schemaNode.isTextual()) {
            this.prefix = schemaNode.asText();
        }
    }


    @Override
    public Set<ValidationMessage> validate(ExecutionContext executionContext, JsonNode node, JsonNode rootNode, JsonNodePath instanceLocation) {
        System.out.println("进入mobilePhone校验器");

        JsonType nodeType = TypeFactory.getValueNodeType(node, this.validationContext.getConfig());
        if (nodeType != JsonType.STRING) {
            // ignore no-string typs
            return Collections.emptySet();
        }

        String textVal = node.textValue();

        if (!StringUtils.hasText(textVal) || !textVal.startsWith(prefix)) {
            return Collections.singleton(message().instanceNode(node).instanceLocation(instanceLocation)
                    .locale(executionContext.getExecutionConfig().getLocale())
                    .failFast(executionContext.isFailFast()).arguments(prefix, textVal).build());
        }

        return Collections.emptySet();
    }

}
JsonSchemaValidatorDemo
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.networknt.schema.*;
import com.networknt.schema.i18n.ResourceBundleMessageSource;
import com.networknt.schema.resource.PrefixSchemaMapper;
import com.zzhua.util.ValidationResult;
import com.zzhua.validator.CustomTypeCode;
import org.springframework.util.StringUtils;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

public class JsonSchemaValidatorDemo {
    public static void main(String[] args) throws JsonProcessingException {

        SchemaValidatorsConfig config = SchemaValidatorsConfig
                .builder()
                .pathType(PathType.URI_REFERENCE)
                .errorMessageKeyword("message")
                .failFast(false)
                // 如果不开启format校验,那么format会被认为只是个注释,而不会去作校验
                .formatAssertionsEnabled(true)
                .messageSource(new ResourceBundleMessageSource("validate/json-validator-message","jsv-messages"))
                .build();



        JsonSchemaFactory schemaFactory = JsonSchemaFactory.getInstance(
                SpecVersion.VersionFlag.V7,
                builder -> {
                    builder.schemaMappers(schemaMapperBuilder -> {
                        schemaMapperBuilder.add(new PrefixSchemaMapper("http://www.example.com", ""));
                    });
                    builder.schemaLoaders(schemaLoaderBuilder -> {
                        schemaLoaderBuilder.add(new CommonSchemaLoader());
                    });
                    builder.metaSchemas(jsonMetaSchemaMap -> {
                        // 1、JsonSchemaFactory$Builder 构建 JsonSchemaFactory(用于维护了一堆的JsonMetaSchema)
                        // 2、JsonSchemaVersion 接口 用于提供 JsonMetaSchema 实例,
                        //    该接口下有 Version7、Version4 等版本,
                        //    比如 Version7 提供了 属于当前版本的 JsonMetaSchema 对 draft07 的json-schema校验支持
                        // 3、此处理解的关键代码在 JsonMetaSchema#createKeywordsMap(Map<String, Keyword> kwords, Map<String, Format> formats)
                        // 4、format规则实际运行参照 FormatKeyword
                        JsonMetaSchema jsonMetaSchemaV7 = jsonMetaSchemaMap.get(JsonMetaSchema.getV7().getIri());
                        if (jsonMetaSchemaV7 != null) {

                            Set<String> keywordsSet = jsonMetaSchemaV7.getKeywords().keySet();
                            System.out.println("keywordsSet#size: " + keywordsSet.size() + ", keywordsSet: " + keywordsSet);

                            JsonMetaSchema jsonMetaSchema = JsonMetaSchema.builder(jsonMetaSchemaV7)
                                    // 自定义format校验
                                    .format(new DateTimeFormat())
                                    .keywords(List.of(CustomTypeCode.CUSTOM_PHONE))
                                    .build();
                            jsonMetaSchemaMap.put(JsonMetaSchema.getV7().getIri(), jsonMetaSchema);
                        }
                    });
                }
        );

        // 1. 加载主 Schema
        InputStream schemaStream = JsonSchemaValidatorDemo.class.getResourceAsStream("/schemas/format-schema.json");

        JsonSchema schema = schemaFactory.getSchema(schemaStream, config);

        // 2. 准备待校验的 JSON 数据
        String jsonData = """
                {
                    "phone": "5662273"
                }
                """;

        // 3. 执行校验
        Set<ValidationMessage> validationMessages = schema.validate(new ObjectMapper().readTree(jsonData));
        List<com.zzhua.util.ValidationResult> results = new ArrayList<>();
        for (ValidationMessage validationMessage : validationMessages) {
            results.add(com.zzhua.util.ValidationResult.builder()
                    .type(validationMessage.getType())
                    .errMsg(validationMessage.getMessage())
                    .propertyPath(deducePropertyPath(validationMessage))
                    .build());
        }

        for (ValidationResult validationResult : results) {
            System.out.println("-------------------");
            System.out.println("校验的关键字: " + validationResult.getType());
            System.out.println("校验的属性: " + validationResult.getPropertyPath());
            System.out.println("校验的错误提示: " + validationResult.getErrMsg());
        }

    }

    private static String deducePropertyPath(ValidationMessage validationMessage) {
        // 推断属性名
        String property = validationMessage.getProperty();
        if (StringUtils.hasText(property)) {
            return property;
        }

        JsonNodePath instanceLocation = validationMessage.getInstanceLocation();
        if (instanceLocation != null) {
            return instanceLocation.toString();
        }

        JsonNodePath evaluationPath = validationMessage.getEvaluationPath();
        if (evaluationPath != null) {
            return evaluationPath.toString();
        }

        return "unknown";
    }
}

示例2(推荐)

JsonSchemaValidatorDemo2
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.networknt.schema.*;
import com.networknt.schema.i18n.ResourceBundleMessageSource;
import com.zzhua.util.ValidationResult;
import org.springframework.util.StringUtils;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;

public class JsonSchemaValidatorDemo2 {

    public static final ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource("validate/json-validator-message", "jsv-messages");

    public static void main(String[] args) throws JsonProcessingException {
        JsonMetaSchema jsonMetaSchemaV7 = JsonMetaSchema.builder(SchemaId.V7)
                .specification(SpecVersion.VersionFlag.V7)
                .idKeyword("$id")
                .formats(Formats.DEFAULT)
                .keywords(ValidatorTypeCode.getKeywords(SpecVersion.VersionFlag.V7))
                .keywords(Arrays.asList(
                        new NonValidationKeyword("$schema"),
                        new NonValidationKeyword("$id"),
                        new AnnotationKeyword("title"),
                        new AnnotationKeyword("description"),
                        new AnnotationKeyword("default"),
                        new NonValidationKeyword("definitions"),
                        new NonValidationKeyword("$comment"),
                        new AnnotationKeyword("examples"),
                        new NonValidationKeyword("then"),
                        new NonValidationKeyword("else"),
                        new NonValidationKeyword("additionalItems")))
                .keyword(new GroovyKeyword())
                .build();

        JsonSchemaFactory jsonSchemaFactory = new JsonSchemaFactory.Builder()
                .defaultMetaSchemaIri(jsonMetaSchemaV7.getIri())
                .metaSchema(jsonMetaSchemaV7)
                .build();

        InputStream schemaStream = JsonSchemaValidatorDemo2.class.getResourceAsStream("/schemas/format-schema.json");


        SchemaValidatorsConfig config = SchemaValidatorsConfig
                .builder()
                .failFast(false)
                .pathType(PathType.URI_REFERENCE)
                .messageSource(messageSource)
                .build();

        JsonSchema schema = jsonSchemaFactory.getSchema(schemaStream, config);

        String jsonData = """
                    {
                      "lang": {
                        "script": "222"
                      }
                    }
                """;

        Set<ValidationMessage> validationMessages = schema.validate(new ObjectMapper().readTree(jsonData));

        List<ValidationResult> results = new ArrayList<>();

        for (ValidationMessage validationMessage : validationMessages) {
            results.add(ValidationResult.builder()
                    .type(validationMessage.getType())
                    .errMsg(validationMessage.getMessage())
                    .propertyPath(deducePropertyPath(validationMessage))
                    .build());
        }

        for (ValidationResult validationResult : results) {
            System.out.println("-------------------");
            System.out.println("校验的关键字: " + validationResult.getType());
            System.out.println("校验的属性: " + validationResult.getPropertyPath());
            System.out.println("校验的错误提示: " + validationResult.getErrMsg());
        }


    }

    private static String deducePropertyPath(ValidationMessage validationMessage) {
        // 推断属性名
        String property = validationMessage.getProperty();
        if (StringUtils.hasText(property)) {
            return property;
        }

        JsonNodePath instanceLocation = validationMessage.getInstanceLocation();
        if (instanceLocation != null) {
            return instanceLocation.toString();
        }

        JsonNodePath evaluationPath = validationMessage.getEvaluationPath();
        if (evaluationPath != null) {
            return evaluationPath.toString();
        }

        return "unknown";
    }

}
GroovyKeyword
import com.fasterxml.jackson.databind.JsonNode;
import com.networknt.schema.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;

import java.util.*;

@Slf4j
public class GroovyKeyword extends AbstractKeyword {

    public GroovyKeyword() {
        super("groovy");
    }

    @Override
    public JsonValidator newValidator(SchemaLocation schemaLocation, JsonNodePath evaluationPath, JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext) throws JsonSchemaException, Exception {
        String configVal = schemaNode.asText();
        return new AbstractJsonValidator(schemaLocation, evaluationPath, this, schemaNode) {

            @Override
            public Set<ValidationMessage> validate(ExecutionContext executionContext, JsonNode node, JsonNode rootNode, JsonNodePath instanceLocation) {

                log.info("GroovyKeyword Validator校验");
                log.info("executionContext: {}", executionContext);
                log.info("node: {}", node);
                log.info("rootNode: {}", rootNode);
                log.info("instanceLocation: {}", instanceLocation);
                log.info("获取到 SchemaValidatorsConfig: {}, 从这可以拿到各种配置", validationContext.getConfig());
                log.info("configVal: {}", configVal);


                if (!node.isTextual()) {
                    // ignore no-string typs
                    return Collections.emptySet();
                }

                String text = node.asText();
                if (StringUtils.hasText(text)) {
                    if (!Objects.equals(configVal, text)) {

                        // 获取提示消息
                        Locale locale = Optional.ofNullable(executionContext.getExecutionConfig().getLocale()).orElse(Locale.ROOT);
                        String tipMessage = JsonSchemaValidatorDemo2.messageSource.getMessage("groovy", locale, configVal, text);

                        return Collections.singleton(
                                ValidationMessage.builder()
                                        .message(tipMessage)
                                        .evaluationPath(instanceLocation)
                                        .type("groovy")
                                        .build()
                        );
                    }
                }

                return Collections.emptySet();
            }
        };
    }
}
resources/schemas/format-schema.json
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "classpath:/schemas/address-schema.json",
  "type": "object",
  "properties": {
    "lang": {
      "properties": {
        "script": {
          "groovy": "1+1"
        }
      }
    }
  }
}
json-validator-message.properties
groovy = 与预设的脚本不一致, 您设置的是:{1}, 应当为:{0}
json-validator-message_zh_CN.properties
groovy = 中文 - 与预设的脚本不一致, 您设置的是:{1}, 应当为:{0}
import com.networknt.schema.PathType;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;

import java.util.Arrays;
import java.util.List;

@Data
@ConfigurationProperties(prefix = "schema.validation")
public class SchemaValidationProperties {

    private Json json;

    @Data
    public static class Json {

        private String prefixSchema = "http://www.example.com";

        private String replacement = "";

        private String schemaLocation = "classpath*:schemas/json/**/*.json";

        private boolean failFast = false;

        private List<String> baseNames = Arrays.asList("validate/json-validator-message","jsv-messages");

        private String errorMessageKeyword = "message";

        private PathType pathType = PathType.URI_REFERENCE;

        private boolean preloadJsonSchema = true;
    }


}
import com.networknt.schema.*;
import com.networknt.schema.i18n.ResourceBundleMessageSource;
import com.zzhua.core.JsonSchemaFactoryBean;
import com.zzhua.core.JsonSchemaFactoryCustomizer;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;

@Configuration
@EnableConfigurationProperties(SchemaValidationProperties.class)
public class JsonSchemaConfiguration {

    private SchemaValidationProperties.Json json;
    private List<JsonSchemaFactoryCustomizer> factoryCustomizers;

    private List<Format> formats;

    private List<Keyword> keywords;

    public JsonSchemaConfiguration(SchemaValidationProperties schemaValidationProperties,
                                   ObjectProvider<List<JsonSchemaFactoryCustomizer>> factoryCustomizers) {
        this.json = schemaValidationProperties.getJson();
        this.factoryCustomizers = factoryCustomizers.getIfAvailable();

    }

    @Autowired
    public void setFormats(List<Format> formats) {
        this.formats = formats;
    }

    @Autowired
    public void setKeywords(List<Keyword> keywords) {
        this.keywords = keywords;
    }

    @Bean
    @ConditionalOnMissingBean(ResourceBundleMessageSource.class)
    public ResourceBundleMessageSource defaultResourceBundleMessageSource() {
        List<String> baseNameList = new ArrayList<>();
        if (CollectionUtils.isEmpty(json.getBaseNames())) {
            // 添加默认的语言包
            baseNameList.add("jsv-message");
        }
        String[] baseNames = baseNameList.toArray(new String[0]);
        return new ResourceBundleMessageSource(baseNames);
    }


    @Bean
    @ConditionalOnMissingBean(SchemaValidatorsConfig.class)
    public SchemaValidatorsConfig defaultSchemaValidatorsConfig(ResourceBundleMessageSource resourceBundleMessageSource) {
        return SchemaValidatorsConfig
                .builder()
                .pathType(json.getPathType())
                .errorMessageKeyword(json.getErrorMessageKeyword())
                .formatAssertionsEnabled(true)
                .failFast(json.isFailFast())
                .preloadJsonSchema(json.isPreloadJsonSchema())
                .messageSource(resourceBundleMessageSource)
                .cacheRefs(true)
                .build();
    }

    @Bean
    @ConditionalOnMissingBean(JsonSchemaFactory.class)
    public JsonSchemaFactoryBean jsonSchemaFactoryV7(SchemaValidatorsConfig schemaValidatorsConfig) {
        JsonSchemaFactoryBean jsonSchemaFactoryBean = new JsonSchemaFactoryBean();
        jsonSchemaFactoryBean.setSchemaValidatorsConfig(schemaValidatorsConfig);
        jsonSchemaFactoryBean.setJson(json);
        jsonSchemaFactoryBean.setFormats(formats);
        jsonSchemaFactoryBean.setKeywords(keywords);
        jsonSchemaFactoryBean.setCustomizers(factoryCustomizers);
        return jsonSchemaFactoryBean;
    }


}

public interface JsonSchemaFactoryCustomizer {
    void customize(JsonSchemaFactory jsonSchemaFactory);
}

import com.networknt.schema.*;
import com.networknt.schema.resource.PrefixSchemaMapper;
import com.zzhua.SchemaValidationProperties;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class JsonSchemaFactoryBean implements FactoryBean<JsonSchemaFactory>, InitializingBean {

    private boolean initialized = false;

    private JsonSchemaFactory jsonSchemaFactory;

    private SchemaValidatorsConfig schemaValidatorsConfig;

    private SchemaValidationProperties.Json json;

    private List<Format> formats;

    private List<Keyword> keywords;

    private List<JsonSchemaFactoryCustomizer> factoryCustomizers;

    public void setSchemaValidatorsConfig(SchemaValidatorsConfig schemaValidatorsConfig) {
        this.schemaValidatorsConfig = schemaValidatorsConfig;
    }

    public void setFormats(List<Format> formats) {
        this.formats = formats;
    }

    public void setKeywords(List<Keyword> keywords) {
        this.keywords = keywords;
    }

    public void setCustomizers(List<JsonSchemaFactoryCustomizer> customizers) {
        this.factoryCustomizers = customizers;
    }

    public void setJson(SchemaValidationProperties.Json json) {
        this.json = json;
    }

    @Override
    public JsonSchemaFactory getObject() {
        return this.jsonSchemaFactory;
    }

    @Override
    public Class<?> getObjectType() {
        return JsonSchemaFactory.class;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        // 避免可能存在的多次初始化调用(比如编码错误)
        if (!initialized) {
            this.jsonSchemaFactory = buildJsonSchemaFactory();
            initialized = true;
        }
    }

    private JsonSchemaFactory buildJsonSchemaFactory() {
        JsonMetaSchema jsonMetaSchemaV7 = JsonMetaSchema.builder(SchemaId.V7)
                .specification(SpecVersion.VersionFlag.V7)
                .idKeyword("$id")
                // 添加内置的format校验
                .formats(Formats.DEFAULT)
                // 添加自定义format
                .formats(formats)
                // 添加v7的关键字
                .keywords(ValidatorTypeCode.getKeywords(SpecVersion.VersionFlag.V7))
                .keywords(Arrays.asList(
                        new NonValidationKeyword("$schema"),
                        new NonValidationKeyword("$id"),
                        new AnnotationKeyword("title"),
                        new AnnotationKeyword("description"),
                        new AnnotationKeyword("default"),
                        new NonValidationKeyword("definitions"),
                        new NonValidationKeyword("$comment"),
                        new AnnotationKeyword("examples"),
                        new NonValidationKeyword("then"),
                        new NonValidationKeyword("else"),
                        new NonValidationKeyword("additionalItems"))
                )
                .keywords(keywords)
                .build();

        JsonSchemaFactory jsonSchemaFactory = new JsonSchemaFactory.Builder()
                .defaultMetaSchemaIri(jsonMetaSchemaV7.getIri())
                .metaSchema(jsonMetaSchemaV7)
                .enableSchemaCache(true)
                .schemaMappers(builder ->
                        builder.add(new PrefixSchemaMapper(json.getPrefixSchema(), json.getReplacement()))
                )
                .schemaLoaders(builder ->
                        builder.add(new CommonSchemaLoader(json.getSchemaLocation()))
                )
                .build();

        if (!ObjectUtils.isEmpty(factoryCustomizers)) {
            factoryCustomizers.forEach(customizer -> customizer.customize(jsonSchemaFactory));
        }

        // 初始化schema
        // 匹配 schemas/json 目录及其子目录下的所有文件
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        Resource[] resources = new Resource[0];
        try {
            resources = resolver.getResources("classpath*:schemas/json/**/*.*");


            // 存储文件相对路径与资源的映射
            Map<String, Resource> fileMap = new HashMap<>();

            for (Resource resource : resources) {
                if (!resource.exists() || !resource.isReadable()) continue;

                // 获取完整的类路径URI
                String uri = resource.getURI().toString();

                // 提取 schemas/json/ 之后的部分作为相对路径
                String pattern = "schemas/json/";
                int index = uri.indexOf(pattern);

                if (index != -1) {
                    // 截取相对路径并标准化
                    String relativePath = uri.substring(index + pattern.length())
                            .replaceAll("/+", "/"); // 处理多余斜杠

                    // 排除目录(Spring 默认不会返回目录,但二次验证)
                    if (!relativePath.endsWith("/")) {
                        fileMap.put(relativePath, resource);
                    }
                }
            }

            if (!CollectionUtils.isEmpty(fileMap)) {
                JsonSchemaCache schemaCache = new JsonSchemaCache();
                for (Map.Entry<String, Resource> entry : fileMap.entrySet()) {
                    JsonSchema schema = jsonSchemaFactory.getSchema(entry.getValue().getInputStream(), schemaValidatorsConfig);
                    schemaCache.add(entry.getKey(), schema);
                }
            }

        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        return jsonSchemaFactory;

    }

    public static void main(String[] args) throws IOException {

        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();

        // 匹配 schemas/json 目录及其子目录下的所有文件
        Resource[] resources = resolver.getResources("classpath*:schemas/json/**/*.*");

        // 存储文件相对路径与资源的映射
        Map<String, Resource> fileMap = new HashMap<>();

        for (Resource resource : resources) {
            if (!resource.exists() || !resource.isReadable()) continue;

            // 获取完整的类路径URI
            String uri = resource.getURI().toString();

            // 提取 schemas/json/ 之后的部分作为相对路径
            String pattern = "schemas/json/";
            int index = uri.indexOf(pattern);

            if (index != -1) {
                // 截取相对路径并标准化
                String relativePath = uri.substring(index + pattern.length())
                        .replaceAll("/+", "/"); // 处理多余斜杠

                // 排除目录(Spring 默认不会返回目录,但二次验证)
                if (!relativePath.endsWith("/")) {
                    fileMap.put(relativePath, resource);
                }
            }
        }

        // 打印结果
        fileMap.forEach((path, res) -> {
            System.out.println("相对路径: " + path);
            System.out.println("完整路径: " + res);
            System.out.println("-----");
        });

    }

}

public class JsonSchemaCache {

    public ConcurrentHashMap<String, JsonSchema> schemaMap = new ConcurrentHashMap<>();

    public void add(String pathKey, JsonSchema jsonSchema) {
        schemaMap.put(pathKey, jsonSchema);
    }

    public JsonSchema get(String pathKey) {
        return schemaMap.get(pathKey);
    }

}
import com.networknt.schema.AbsoluteIri;
import com.networknt.schema.resource.InputStreamSource;
import com.networknt.schema.resource.SchemaLoader;
import com.zzhua.SchemaValidationProperties;

import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.function.Supplier;

public class CommonSchemaLoader implements SchemaLoader {

    private final Supplier<ClassLoader> classLoaderSource;

    private final String schemaLocation;

    /**
     * Constructor.
     */
    public CommonSchemaLoader(String schemaLocation) {
        this(CommonSchemaLoader::getClassLoader, schemaLocation);
    }

    public CommonSchemaLoader(Supplier<ClassLoader> classLoaderSource, String schemaLocation) {
        this.classLoaderSource = classLoaderSource;
        this.schemaLocation = schemaLocation;
    }

    @Override
    public InputStreamSource getSchema(AbsoluteIri absoluteIri) {
        String iri = absoluteIri != null ? absoluteIri.toString() : "";
        String name = null;
        if (iri.startsWith("/schemas")) {
            name = iri.substring(1);
        }
        if (name != null) {
            ClassLoader classLoader = this.classLoaderSource.get();
            String resource = name;
            return () -> {
                InputStream result = classLoader.getResourceAsStream(resource);
                if (result == null) {
                    result = classLoader.getResourceAsStream(resource.substring(1));
                }
                if (result == null) {
                    throw new FileNotFoundException(iri);
                }
                return result;
            };
        }
        return null;
    }

    protected static ClassLoader getClassLoader() {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        if (classLoader == null) {
            classLoader = SchemaLoader.class.getClassLoader();
        }
        return classLoader;
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2380182.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

交易所开发:构建功能完备的金融基础设施全流程指南

交易所开发&#xff1a;构建功能完备的金融基础设施全流程指南 ——从技术架构到合规安全的系统性解决方案 一、开发流程&#xff1a;从需求分析到运维优化 开发一款功能完备的交易所需要遵循全生命周期管理理念&#xff0c;涵盖市场定位、技术实现、安全防护和持续迭代四大阶…

Axure疑难杂症:统计分析页面引入Echarts示例动态效果

亲爱的小伙伴,在您浏览之前,烦请关注一下,在此深表感谢! Axure产品经理精品视频课已登录CSDN可点击学习https://edu.csdn.net/course/detail/40420 课程主题:统计分析页面引入Echarts示例动态效果 主要内容:echart示例引入、大小调整、数据导入 应用场景:统计分析页面…

展锐Android14及更新版本split_build编译方法

更改split_build.py文件内容后按照下面方法编译&#xff1a; zip -r sys/vendor/sprd/release/split_build.zip sys/vendor/sprd/release/split_build/ rm -r sys/vendor/sprd/release/split_build/ cp -r vnd/vendor/sprd/release/split_build/ sys/vendor/sprd/release/cd s…

青少年ctf平台应急响应-应急响应2

题目&#xff1a; 当前服务器被创建了一个新的用户&#xff0c;请提交新用户的用户名&#xff0c;得到的结果 ssh rootchallenge.qsnctf.com -p 30327 这个命令用于通过 SSH 协议连接到指定的远程服务器。具体解释如下&#xff1a; ssh&#xff1a;这是在 Unix-like 系统中…

k8s监控方案实践补充(二):使用kube-state-metrics获取资源状态指标

k8s监控方案实践补充&#xff08;二&#xff09;&#xff1a;使用kube-state-metrics获取资源状态指标 文章目录 k8s监控方案实践补充&#xff08;二&#xff09;&#xff1a;使用kube-state-metrics获取资源状态指标一、Metrics Server简介二、kube-state-metrics实战部署1. 创…

基于SpringBoot的小型民营加油站管理系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏&#xff1a;…

技术架构缺乏灵活性,如何应对变化需求?

技术架构缺乏灵活性会导致企业在面临市场变化、用户需求演化或新技术出现时难以及时响应&#xff0c;直接影响产品更新速度与竞争力。要有效应对变化需求&#xff0c;需要从引入模块化架构设计、推动微服务拆分、加强架构治理与决策机制、构建中台与平台化能力等方面系统推进。…

【AI时代】Java程序员大模型应用开发详细教程(上)

目录 一、大模型介绍 1. 大模型介绍 1.1 什么是大模型 1.2 技术储备 1.3 大模型的分类 2. 入门案例 3.Token的介绍 二、提示词工程 1. 好玩的提示词案例 1.1 翻译软件 1.2 让Deepseek绘画 1.3 生成数据 1.4 代码生成 2. 提示词介绍 3. Prompt Engineering最佳实…

虚拟网络编辑器

vmnet1 仅主机模式 hostonly 功能&#xff1a;虚拟机只能和宿主机通过vmnet1通信&#xff0c;不可连接其他网络&#xff08;包括互联网&#xff09; vmnet8 地址转换模式 NAT 功能&#xff1a;虚拟机可以和宿主通过vmnet8通信&#xff0c;并且可以连接其他网络&#xff0c;但是…

第31讲 循环缓冲区与命令解析

串口在持续接收数据时容易发生数据黏包&#xff08;先接收的数据尚未被处理&#xff0c;后面的数据已经将内存覆盖&#xff09;的情况&#xff0c;循环缓冲区的本质就是将串口接受到的数据马上拷贝到另外一块内存之中。为了避免新来的数据覆盖掉尚未处理的数据&#xff0c;一方…

数据结构(十)——排序

一、选择排序 1.简单选择排序 基本思想&#xff1a;假设排序表为[1,…,n]&#xff0c;第i趟排序即从[i,…,n]中选择关键字最小的元素与L[i]交换 eg&#xff1a;给定关键字序列{87&#xff0c;45&#xff0c;78&#xff0c;32&#xff0c;17&#xff0c;65&#xff0c;53&…

美蛋工具箱:一站式解决图片、视频、音频和文档处理需求的聚合神器

先放下载链接:夸克网盘下载 宝子们&#xff0c;今天不啰嗦&#xff0c;直接给大家安利一款超好用的聚合工具&#xff0c;有需要的小伙伴赶紧码住&#xff01; 今天要介绍的这款工具叫美蛋工具箱&#xff0c;它是一款聚合类工具。这个软件是绿色版的&#xff0c;聚合了图片工具…

python打卡day16

NumPy 数组基础 因为前天说了shap&#xff0c;这里涉及到数据形状尺寸问题&#xff0c;所以需要在这一节说清楚&#xff0c;后续的神经网络我们将要和他天天打交道。 知识点&#xff1a; numpy数组的创建&#xff1a;简单创建、随机创建、遍历、运算numpy数组的索引&#xff1a…

Redis 学习笔记 5:分布式锁

Redis 学习笔记 5&#xff1a;分布式锁 在前文中学习了如何基于 Redis 创建一个简单的分布式锁。虽然在大多数情况下这个锁已经可以满足需要&#xff0c;但其依然存在以下缺陷&#xff1a; 事实上一般而言&#xff0c;我们可以直接使用 Redisson 提供的分布式锁而非自己创建。…

游戏开发实战(一):Python复刻「崩坏星穹铁道」嗷呜嗷呜事务所---源码级解析该小游戏背后的算法与设计模式【纯原创】

文章目录 奇美拉项目游戏规则奇美拉(Chimeras)档案领队成员 结果展示&#xff1a; 奇美拉项目 由于项目工程较大&#xff0c;并且我打算把我的思考过程和实现过程中踩过的坑都分享一下&#xff0c;因此会分3-4篇博文详细讲解本项目。本文首先介绍下游戏规则并给出奇美拉档案。…

02- 浏览器运行原理

文章目录 1. 网页的解析过程浏览器内核 2. 浏览器渲染流程2.1 解析html2.2 生成css规则2.3 构建render tree2.4 布局(Layout)2.5 绘制(Paint) 3. 回流和重绘3.1 回流reflow&#xff08;1&#xff09;理解&#xff1a;&#xff08;2&#xff09;出现情况 3.2 重绘repaint&#x…

移除链表元素数据结构oj题(力扣题206)

目录 题目描述&#xff1a; 题目解读&#xff08;分析&#xff09; 解决代码 题目描述&#xff1a; 给你一个链表的头节点 head 和一个整数 val &#xff0c;请你删除链表中所有满足 Node.val val 的节点&#xff0c;并返回 新的头节点 。 题目解读&#xff08;分析&#…

OpenTelemetry 从入门到精通

快速入门 OpenTelemetry 是一个可观测性框架和工具包&#xff0c; 旨在创建和管理遥测数据&#xff0c;如链路、 指标和日志。 重要的是&#xff0c;OpenTelemetry 是供应商和工具无关的&#xff0c;这意味着它可以与各种可观测性后端一起使用&#xff0c; 包括 Jaeger 和 Pro…

【免杀】C2免杀技术(六)进程镂空(傀儡进程)

一、技术定位与核心思想 进程镂空&#xff08;Process Hollowing&#xff09;属于 MITRE ATT&CK 中 T1055.012 子技术&#xff1a;先创建一个合法进程并挂起&#xff0c;随后把其主模块从内存“掏空”并替换为恶意映像&#xff0c;最后恢复线程执行&#xff0c;从而让…

Eclipse Java 开发调优:如何让 Eclipse 运行更快?

Eclipse Java 开发调优&#xff1a;如何让 Eclipse 运行更快&#xff1f; 在 Java 开发领域&#xff0c;Eclipse 是一款被广泛使用的集成开发环境&#xff08;IDE&#xff09;。然而&#xff0c;随着项目的日益庞大和复杂&#xff0c;Eclipse 的运行速度可能会逐渐变慢&#x…