知識庫 / JSON / Jackson RSS 訂閱

Java程序化JSON Schema生成

Jackson
HongKong
9
09:44 PM · Dec 05 ,2025

1. 概述

本教程將使用 Java 語言和 Java JSON Schema Generator 庫,生成 JSON 模式。

首先,我們將學習如何生成簡單的和遞歸的 JSON 模式。接下來,我們將探討可用的不同模式配置。然後,我們將逐步從庫的模塊中推導出 JSON 模式:JacksonJakarta validation。最後,我們將設置一個 Maven 插件,以便在 Maven 的 generate 目標中生成 JSON 模式。

2. 安裝準備

讓我們為我們的項目設置必要的依賴項。

2.1. 核心依賴

首先,讓我們安裝 jsonschema-generator

<dependency>
    <groupId>com.github.victools</groupId>
    <artifactId>jsonschema-generator</artifactId>
    <version>4.31.1</version>
</dependency>

它包含用於模式生成和配置的主要API。

2.2. 模塊

接下來,我們將安裝三個模塊,用於從類註解中生成 JSON Schema 屬性。首先,我們添加 jsonschema-module-jackson

<dependency>
    <groupId>com.github.victools</groupId>
    <artifactId>jsonschema-module-jackson</artifactId>
    <version>4.31.1</version>
</dependency>

本模塊從 Jackson 註解中派生 JSON Schema 屬性。

接下來,我們將安裝 jsonschema-module-jakarta-validation 以從 Jakarta 驗證註解中獲取 Schema:

<dependency>
    <groupId>com.github.victools</groupId>
    <artifactId>jsonschema-module-jakarta-validation</artifactId>
    <version>4.31.1</version>
</dependency>

2.3 Maven 插件

最後,讓我們添加 《jsonschema-maven-plugin》

<plugin>
    <groupId>com.github.victools</groupId>
    <artifactId>jsonschema-maven-plugin</artifactId>
    <version>4.31.1</version>
    <executions>
        <execution>
            <goals>
                <goal>generate</goal>
            </goals>
        </execution>
    </executions>
</plugin>

稍後,我們將定義 配置 選項。它接受用於生成模式的類、模式配置以及要使用的模塊。

請注意,自 Java JSON Schema Generator 4.7 版本起,強烈建議模塊和插件使用與核心依賴相同的版本。

3. 基礎

本節將探討 <a href="https://mvnrepository.com/artifact/com.github.victools/jsonschema-generator">jsonschema-generator</a> 的構建模塊,通過創建簡單的遞歸模式。

3.1. 簡單模式

讓我們定義一個 文章

public class Article { 
    private UUID id;
    private String title;
    private String content;
    private Date createdAt;
    private Area area;
    // getters and setters omitted
 }

我們將會從 Article 類生成一個模式:

SchemaGeneratorConfigBuilder configBuilder = new SchemaGeneratorConfigBuilder(SchemaVersion.DRAFT_2020_12, OptionPreset.PLAIN_JSON);
SchemaGeneratorConfig config = configBuilder.with(Option.EXTRA_OPEN_API_FORMAT_VALUES)
  .without(Option.FLATTENED_ENUMS_FROM_TOSTRING)
  .build();

SchemaGenerator generator = new SchemaGenerator(config);
JsonNode jsonSchema = generator.generateSchema(Article.class);

在此,我們正在針對 DRAFT_2020-12,即當前最新的 JSON Schema 草案。如果未指定,則模式將使用 DRAFT-7 規範生成。

PLAIN_JSON OptionPreset 包含大量默認配置,用於生成每個非靜態類字段的模式。其他可用的預設包括 JAVA_OBJECTFULL_DOCUMENTATION。前者包括公共字段和方法在模式中的,後者則包括所有字段和公共方法。 如果未指定,則預設默認為 FULL_DOCUMENTATION

生成的模式尊重 DRAFT_2020-12 結構:

{
    "$schema":"https://json-schema.org/draft/2020-12/schema",
    "type":"object",
    "properties":{
        "area":{
            "type":"string",
            "enum":[
                "JAVA",
                "KOTLIN",
                "SCALA",
                "LINUX"
            ]
        },
        "content":{
            "type":"string"
        },
        "createdAt":{
            "type":"string",
            "format":"date-time"
        },
        "id":{
            "type":"string",
            "format":"uuid"
        },
        "title":{
            "type":"string"
        }
    }
}

首先,請注意以下幾點。首先,Java 的 DateUUID 在模式中都是字符串。幸運的是,它們的真實類型是在 字段格式中指定的,這要歸功於 EXTRA_OPEN_API_FORMAT_VALUES 生成器選項。它為特殊 JSON Schema 字符串添加了額外信息。最後,Java 枚舉通過調用它們的 name() 方法來表示。

3.2. 遞歸模式

讓我們定義一個 作者 類:

public class Author {
    private UUID id;
    private String name;
    private String role;
    private List<AuthoredArticle> articles;
    // getters, setters, omitted
}

一位 作者 擁有一個 已撰寫文章 的列表。反之,一個 已撰寫文章 擁有一位 作者

public class AuthoredArticle {
    private Author author;
    // getters and setters omitted
}

保留上一節所有配置,AuthoredArticle類的數據模式是一個遞歸模式

有趣的是,author屬性中的articles字段引用了正在生成的實際模式:

{
    "author":{
        "type":"object",
        "properties":{
            "articles":{
                "type":"array",
                "items":{
                    "$ref":"#"
                }
            }
        }
    }
}

這種循環引用是被規範允許的。但是,<$ref> 不能指向另一個 <$ref>。

4. 配置

在上一部分,我們使用了一些內置預設。現在,我們將學習如何實現精細的配置。

首先,我們將使用 單獨配置來定製生成的模式屬性。然後,我們將預覽 高級配置

4.1. 個體配置

讓我們為我們的 作者 類配置 模式字段

configBuilder.forFields()
  .withRequiredCheck(field -> field.getAnnotationConsideringFieldAndGetter(Nullable.class) == null)
  .withArrayUniqueItemsResolver(scope -> scope.getType().getErasedType() == (List.class) ? true : null);

結果模式標記出非可空屬性為必填屬性。它還使 articles 屬性成為唯一數組:

{
    "$schema":"https://json-schema.org/draft/2020-12/schema",
    "type":"object",
    "properties":{
        "articles":{
            "uniqueItems":true,
            "type":"array",
            "items":{
                "type":"object",
                "properties":{
                    "area":{
                        "type":"string",
                        "enum":[
                            "JAVA",
                            "KOTLIN",
                            "SCALA",
                            "LINUX"
                        ]
                    },
                    "author":{
                        "$ref":"#"
                    },
                    "content":{
                        "type":"string"
                    },
                    "createdAt":{
                        "type":"string",
                        "format":"date-time",
                        "default":1690565063847
                    },
                    "id":{
                        "type":"string",
                        "format":"uuid"
                    },
                    "title":{
                        "type":"string"
                    }
                },
                "required":[
                    "area",
                    "author",
                    "content",
                    "createdAt",
                    "id",
                    "title"
                ]
            },
            "default":[
                
            ]
        },
        "id":{
            "type":"string",
            "format":"uuid"
        },
        "name":{
            "type":"string"
        },
        "role":{
            "type":"string"
        }
    },
    "required":[
        "articles",
        "id",
        "name",
        "role"
    ]
}

上面的模式也為 createdAtarticles 屬性設置了默認值。這是由於我們配置了類型造成的:

configBuilder.forTypesInGeneral()
  .withArrayUniqueItemsResolver(scope -> scope.getType().getErasedType() == (List.class) ? true : null)
  .withDefaultResolver(scope -> scope.getType().getErasedType() == List.class ? Collections.EMPTY_LIST : null)
  .withDefaultResolver(scope -> scope.getType().getErasedType() == Date.class ? Date.from(Instant.now()) : null);

ArrayUniqueItemsResolver 確保如果數組是從 List 類型生成的,則將其標記為唯一項。

正如我們配置了字段和類型一樣,我們也可以配置方法:

configBuilder.forMethods()
  .withRequiredCheck(method -> method.getAnnotationConsideringFieldAndGetter(NotNull.class) != null);

我們標記被標註為 的字段為必填字段。如果該註解應用於它們的getter,則它們也將被標記為必填字段。

此外,對於每個配置,返回 不會設置該字段在模式中的有效性。

4.2. 高級配置

在本節中,我們將使用我們的 AdvancedArticle 類:

public class AdvancedArticle {
    private UUID id;
    private String title;
    private String content;
    @AllowedTypes({Timestamp.class, String.class, Date.class})
    private Object createdAt;
    private Area area;
    // getters and setters omitted 
}

高級配置是定製 JSON Schema 生成的終極手段。它特別適用於我們需要通過配置無法提供的屬性的情況:

configBuilder.forFields()
  .withInstanceAttributeOverride((node, field, context) -> node.put("readOnly", field.getDeclaredType().isInstanceOf(UUID.class)));

在這裏,我們為每個屬性都添加了 readOnly 屬性。 默認值為 false,除非是 UUID 類。

{
    "id":{
        "type":"string",
        "format":"uuid",
        "readOnly":true
    },
    "title":{
        "type":"string",
        "readOnly":false
    }
}

另一個有趣的配置選項是能夠在給定字段中指定允許的類型。在我們的 AdvancedArticle 類中,createdAt 屬性同時接受 DateTimestamp 類型:

configBuilder.forFields()
  .withTargetTypeOverridesResolver(field -> Optional.ofNullable(field.getAnnotationConsideringFieldAndGetterIfSupported(AllowedTypes.class))
    .map(AllowedTypes::value)
    .map(Stream::of)
    .map(stream -> stream.map(subtype -> field.getContext().resolve(subtype)))
    .map(stream -> stream.collect(Collectors.toList()))
    .orElse(null));

在底層,TargetTypeOverride 類會處理所有標記為 @AllowedTypes 的字段。然後,它會將這些類型添加到生成的 createdAt 屬性中:

{
    "createdAt":{
        "anyOf":[
            {
                "type":"object",
                "properties":{
                    "nanos":{
                        "type":"integer",
                        "format":"int32",
                        "readOnly":false
                    }
                },
                "readOnly":false
            },
            {
                "type":"string",
                "format":"date-time",
                "readOnly":false
            }
        ]
    }
}

如我們所見,結果的聯合類型由 anyOf 屬性指定。

請記住,配置的可能性是無限的。我們甚至可以添加 自定義類型定義自定義屬性定義。 關鍵在於我們選擇哪種程度的自定義來滿足我們的需求。

5. 模塊

Java JSON Schema Generator 允許我們通過 模塊 將配置項進行分組。 我們可以通過實現 Module 接口 來創建自己的模塊。 在下一部分,我們將看到如何使用一些 內置模塊。 我們將探索 JacksonJakarta Validation 模塊。

5.1. Jackson

Jackson 模塊 通過處理 Jackson 註解來生成 JSON Schema 配置。 讓我們考慮我們的 Person 類:

class Person {

    @JsonProperty(access = JsonProperty.Access.READ_ONLY)
    UUID id;

    @JsonProperty(access = JsonProperty.Access.READ_WRITE, required = true)
    String name;

    @JsonProperty(access = JsonProperty.Access.READ_WRITE, required = true)
    String surname;

    @JsonProperty(access = JsonProperty.Access.READ_WRITE, required = true)
    Address address;

    @JsonIgnore
    String fullName;

    @JsonProperty(access = JsonProperty.Access.READ_ONLY)
    Date createdAt;

    @JsonProperty(access = JsonProperty.Access.READ_WRITE)
    List<Person> friends;
    //getters and setters omitted
}

讓我們將 JacksonModule 添加到我們的 SchemaGeneratorConfigBuilder 中:

JacksonModule module = new JacksonModule(RESPECT_JSONPROPERTY_REQUIRED);
SchemaGeneratorConfigBuilder configBuilder = new SchemaGeneratorConfigBuilder(DRAFT_2020_12, PLAIN_JSON).with(module)
  .with(EXTRA_OPEN_API_FORMAT_VALUES);

SchemaGenerator generator = new SchemaGenerator(configBuilder.build());
JsonNode jsonSchema = generator.generateSchema(Person.class);

該模塊接受某些選項以進行進一步自定義。 RESPECT_JSONPROPERTY_REQUIRED 選項指示模塊應在模式的 readOnly 字段生成過程中考慮 JsonProperty.Access

生成的模式已正確設置 requiredreadOnly 字段:

{
    "type":"object",
    "properties":{
        "createdAt":{
            "type":"string",
            "format":"date-time",
            "readOnly":true
        },
        "friends":{
            "type":"array",
            "items":{
                "$ref":"#"
            }
        },
        "id":{
            "type":"string",
            "format":"uuid",
            "readOnly":true
        }
    },
    "required":[
        "address",
        "name",
        "surname"
    ]
}

未註釋的屬性和帶有@JsonIgnore註解的屬性將被忽略。 嵌套的Address類字段以及friends屬性的遞歸模式均得到正確引用。

5.2. Jakarta Validation

Jakarta Validation 模塊jakarta.validation.constraints 註解生成模式配置。 讓我們為 Person 類進行裝飾:

class Person {

    @NotNull
    UUID id;

    @NotNull
    String name;

    @NotNull
    @Email
    @Pattern(regexp = "\\b[A-Za-z0-9._%+-]+@baeldung\\.com\\b")
    String email;

    @NotNull
    String surname;

    @NotNull
    Address address;

    @Null
    String fullName;

    @NotNull
    Date createdAt;

    @Size(max = 10)
    List<Person> friends;
    //getters and setters omitted

}

接下來,讓我們配置 JakartaValidationModule

JakartaValidationModule module = new JakartaValidationModule(NOT_NULLABLE_FIELD_IS_REQUIRED, INCLUDE_PATTERN_EXPRESSIONS);
SchemaGeneratorConfigBuilder configBuilder = new SchemaGeneratorConfigBuilder(DRAFT_2020_12, PLAIN_JSON).with(module);

SchemaGenerator generator = new SchemaGenerator(configBuilder.build());
JsonNode jsonSchema = generator.generateSchema(Person.class);

模塊可以選擇通過其 forValidationGroups() 方法傳遞驗證組。

NOT_NULLABLE_FIELD_IS_REQUIRED 選項使帶有 @NotNull 註解的字段強制為必填項。 藉助 INCLUDE_PATTERN_EXPRESSIONS,生成的模式包含所有帶有 @Pattern 註解的屬性的模式字段。

{
    "type":"object",
    "properties":{
        "createdAt":{
            "type":"string"
        },
        "email":{
            "type":"string",
            "format":"email",
            "pattern":"\\b[A-Za-z0-9._%+-]+@baeldung\\.com\\b"
        },
        "friends":{
            "maxItems":10,
            "type":"array",
            "items":{
                "$ref":"#"
            }
        },
        "fullName":{
            "type":[
                "string",
                "null"
            ]
        }
    },
    "required":[
        "createdAt",
        "email",
        "id",
        "name",
        "surname"
    ]
}

請注意,email 屬性由於在 Person 類中使用了 @Email 註解,因此具有 format 字段。同樣,friends 屬性的 maxItems 字段也已正確設置。

6. Maven 插件

Java JSON Schema Generator 提供一個 Maven 插件,用於從我們的構建流程中生成 Schema。 讓我們配置該插件:

<plugin>
    <groupId>com.github.victools</groupId>
    <artifactId>jsonschema-maven-plugin</artifactId>
    <version>4.31.1</version>
    <executions>
        <execution>
            <goals>
                <goal>generate</goal>
            </goals>
        </execution>
    </executions>
    <configuration>
        <packageNames>
            <packageName>com.baeldung.jsonschemageneration.plugin</packageName>
        </packageNames>
        <classNames>
            <className>com.baeldung.jsonschemageneration.plugin.Person</className>
        </classNames>
        <schemaVersion>DRAFT_2020_12</schemaVersion>
        <schemaFilePath>src/main/resources/schemas</schemaFilePath>
        <schemaFileName>{1}/{0}.json</schemaFileName>
        <failIfNoClassesMatch>true</failIfNoClassesMatch>
        <options>
            <preset>PLAIN_JSON</preset>
            <enabled>
                <option>DEFINITIONS_FOR_ALL_OBJECTS</option>
                <option>FORBIDDEN_ADDITIONAL_PROPERTIES_BY_DEFAULT</option>
            </enabled>
            <disabled>SCHEMA_VERSION_INDICATOR</disabled>
        </options>
        <modules>
            <module>
                <name>Jackson</name>
                <options>
                    <option>RESPECT_JSONPROPERTY_REQUIRED</option>
                </options>
            </module>
            <module>
                <name>JakartaValidation</name>
                <options>
                    <option>NOT_NULLABLE_FIELD_IS_REQUIRED</option>
                    <option>INCLUDE_PATTERN_EXPRESSIONS</option>
                </options>
            </module>
        </modules>
    </configuration>
</plugin>

我們將基於位於 com.baeldung.jsonschemageneration.plugin 包中的 Person 類生成一個模式。 我們可以仍然定義要使用的模塊,並向它們傳遞一些選項。 但是,該插件不允許為自定義模塊配置選項。

最後,生成的文件的命名模式由 {1}(即包名)和 {0}(即類名)組成。 它位於 src\main\resources\schemas\com\baeldung\jsonschemageneration\plugin\Person.json。 要生成它,讓我們運行 mvn compile

生成的模式尊重插件配置中指定的所有條件。

7. 結論

在本文中,我們使用了 Java JSON Schema Generator 生成 Java 中的 JSON Schema。 在對 Schema 生成的基本概念的學習之後,我們瞭解瞭如何調整 Schema 配置。 隨後,我們探索了庫中的各種模塊以生成 JSON Schema。 最終,我們通過使用專門插件,將 JSON Schema 作為 Maven 的 generate 目標的一部分創建出來。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.