CodeGym /课程 /JAVA 25 SELF /JSON 验证:JSON Schema,与验证错误

JSON 验证:JSON Schema,与验证错误

JAVA 25 SELF
第 46 级 , 课程 4
可用

1. 为什么需要 JSON 验证

想象一下:你编写了一个 User 类,并且期望总是收到如下 JSON 作为输入:

{
  "id": 42,
  "name": "Alice",
  "email": "alice@example.com"
}

但突然你收到了这样的数据:

{
  "id": "四十二",
  "name": 123,
  "email": null,
  "admin": true
}

或者甚至是:

{
  "username": "Alice"
}

在较好的情况下,JacksonGson 会在尝试反序列化时抛出异常。更糟的是——它们可能会默默地把字段赋予默认值,从而让你的业务代码出现不正确的行为。如果这是你的服务配置——就可能引发一些“有趣”的 bug,最后需要整个团队一起排查。

JSON 验证 是一个检查过程,用于确保 JSON 中的数据结构、类型和值符合既定规则(模式)。这就像数据的边检:通不过——就不让上船!

2. JSON Schema:是什么以及长什么样

在 JSON 世界里有一个用于描述数据结构的官方标准——JSON Schema。它就像一个“检查清单”,可以用来验证某个 JSON 是否满足你程序的要求。

JSON Schema 本身也是 JSON,只是使用了一些特殊的键:typepropertiesrequired 等等。

最简单的模式示例

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "id":    { "type": "integer" },
    "name":  { "type": "string" },
    "email": { "type": "string", "format": "email" }
  },
  "required": ["id", "name"]
}

这里发生了什么:

  • 期望的是一个对象(type: "object")。
  • 对象可以包含字段 "id""name""email"(在 properties 中描述)。
  • "id" —— 必须为整数(type: "integer")。
  • "name" —— 必须为字符串(type: "string")。
  • "email" —— 必须是形如 email 的字符串(键 format 的值为 "email")。
  • required 指定必填字段列表:"id""name"

如果 JSON 缺少 "id""name",或者它们的类型不匹配——验证将无法通过。

JSON Schema 的能力概览

  • 指定类型(type: "string""integer""array""object""boolean""null")。
  • 描述嵌套对象和数组(propertiesitems)。
  • 必填与可选字段(required)。
  • 检查字符串长度、数值范围(minLengthmaximum 等)。
  • 格式校验(format: "email""date""uri",等)。
  • 枚举(enum: 允许值列表)。
  • 字符串的正则校验(pattern)。
  • 复杂条件:anyOfoneOfallOf(用于更高级的场景)。

3. 在 Java 中进行 JSON 验证:库概览

Java 标准库并不包含基于模式的 JSON 验证,但有一些流行的第三方库。这里是最常见的:

  • everit-org/json-schema —— 简单、免费、流行。
  • networknt/json-schema-validator —— 速度快,支持最新标准。
  • Jackson-module-jsonSchema —— Jackson 的扩展(但不支持完整的验证)。
  • JustifyJava JSON Tools —— 还有其他选择,但使用较少。

本讲我们将使用 everit-org/json-schema —— 它对新手友好、文档完善,而且无需“折腾”。

安装 everit-org/json-schema

将依赖添加到你的 pom.xmlMaven):

<dependency>
  <groupId>org.everit.json</groupId>
  <artifactId>org.everit.json.schema</artifactId>
  <version>1.14.2</version>
</dependency>

或通过 Gradle

implementation 'org.everit.json:org.everit.json.schema:1.14.2'

4. 示例:按模式验证 JSON(步骤演示)

我们来实际验证一次 JSON。需要准备模式文件、JSON 数据以及一些代码。

模式示例(user-schema.json):

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "id":    { "type": "integer" },
    "name":  { "type": "string", "minLength": 2, "maxLength": 30 },
    "email": { "type": "string", "format": "email" }
  },
  "required": ["id", "name"]
}

有效 JSON 示例(user.json):

{
  "id": 1,
  "name": "Alice",
  "email": "alice@example.com"
}

无效 JSON 示例:

{
  "id": "一",
  "name": "",
  "email": "not-an-email"
}

验证代码

import org.everit.json.schema.Schema;
import org.everit.json.schema.loader.SchemaLoader;
import org.json.JSONObject;
import org.json.JSONException;
import org.json.JSONTokener;
import org.everit.json.schema.ValidationException;

import java.nio.file.Files;
import java.nio.file.Paths;

public class JsonValidationExample {
    public static void main(String[] args) throws Exception {
        // 从文件加载模式
        String schemaString = new String(Files.readAllBytes(Paths.get("user-schema.json")));
        JSONObject rawSchema = new JSONObject(new JSONTokener(schemaString));
        Schema schema = SchemaLoader.load(rawSchema);

        // 加载待校验的 JSON
        String jsonString = new String(Files.readAllBytes(Paths.get("user.json")));
        JSONObject json = new JSONObject(new JSONTokener(jsonString));

        // 验证
        try {
            schema.validate(json); // 如果一切正常——不会发生任何事情
            System.out.println("JSON 有效!");
        } catch (ValidationException e) {
            System.out.println("JSON 无效!");
            for (String msg : e.getAllMessages()) {
                System.out.println("错误: " + msg);
            }
        }
    }
}

代码说明:

  • 使用 Schema 类与加载器 SchemaLoader.load(...)
  • 方法 schema.validate(json) 在与模式不匹配时会抛出异常。
  • catch 块中,可通过 getAllMessages() 获取所有错误信息。

如何集成到应用中?

通常把模式文件放在资源目录(例如 resources 文件夹)中。你应该在将 JSON 反序列化为 Java 对象之前先进行验证。如果一切正常——再反序列化并继续后续处理。

5. 处理验证错误

当 JSON 未通过校验时,库会抛出 ValidationException。异常消息包含错误列表:具体哪里不符合模式。

错误输出示例

对于上面的无效 JSON,输出大致如下:

JSON 无效!
错误: #: required key [id] not found
错误: #/name: expected minLength: 2, actual: 0
错误: #/email: String [not-an-email] is invalid against requested format [email]
错误: #/id: expected type: Integer, found: String

如何解读这些错误:

  • required key [id] not found —— 缺少必填字段。
  • expected minLength: 2, actual: 0 —— 字符串太短。
  • String [...] is invalid against requested format [email] —— email 格式不正确。
  • expected type: Integer, found: String —— 类型不匹配。

重要! 消息可能是英文的,但含义非常直观。

如何向用户展示错误?

你可以把错误收集成列表,通过 REST API 或 GUI 返回给用户,这样能快速定位输入数据的问题。

6. 实战:验证对象数组

很多时候需要验证的不只是一个对象,而是一个数组:

[
  { "id": 1, "name": "Alice", "email": "alice@example.com" },
  { "id": 2, "name": "Bob" },
  { "id": "这是什么?", "name": 123, "email": "not-an-email" }
]

模式:

{
  "type": "array",
  "items": {
    "type": "object",
    "properties": {
      "id":    { "type": "integer" },
      "name":  { "type": "string", "minLength": 2, "maxLength": 30 },
      "email": { "type": "string", "format": "email" }
    },
    "required": ["id", "name"]
  }
}

验证 的方式相同,只是传入的 JSON 是数组。错误消息会包含有问题元素的索引(例如 [#/2/id])。

7. JSON 验证中的常见错误

错误 №1:数据类型不匹配。 很常见的情况是收到字符串而不是数字(如 "id": "123"),而模式期望 integer。校验会失败。如果你无法控制数据来源——要么修改模式,要么提前转换数据。

错误 №2:缺少必填字段。 如果模式中声明了必填字段("required": ["id","name"]),而 JSON 没有该字段——就会报错。有时这源于意外:前端忘记发送字段,或 API 发生了变更。

错误 №3:JSON 中存在多余字段。 默认情况下,JSON Schema 允许多余字段。如果你需要严格的模式,不要忘了添加 "additionalProperties": false。否则 JSON 可以包含任意“无关”的字段。

错误 №4:模式版本或语法不正确。 如果你使用了当前模式版本不存在的键,或者出现拼写错误,验证器将无法加载模式。请在 https://www.jsonschemavalidator.net/ 或类似服务上检查你的模式。

错误 №5:错误处理不佳。 如果只捕获第一个异常且不向用户展示详细信息,就很难知道 JSON 究竟哪里有问题。使用 getAllMessages() 来输出所有错误。

错误 №6:格式校验过于严格或不够严格。"format": "email""date" 的校验有时比较“宽松”。如果你需要严格的校验,请在代码中增加额外检查。

1
调查/小测验
JSON 序列化第 46 级,课程 4
不可用
JSON 序列化
JSON 序列化
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION