1. 概述
本教程將使用 Java 語言和 Java JSON Schema Generator 庫,生成 JSON 模式。
首先,我們將學習如何生成簡單的和遞歸的 JSON 模式。接下來,我們將探討可用的不同模式配置。然後,我們將逐步從庫的模塊中推導出 JSON 模式:Jackson 和 Jakarta 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_OBJECT 和 FULL_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 的 Date 和 UUID 在模式中都是字符串。幸運的是,它們的真實類型是在 字段格式中指定的,這要歸功於 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"
]
}上面的模式也為 createdAt 和 articles 屬性設置了默認值。這是由於我們配置了類型造成的:
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 屬性同時接受 Date 和 Timestamp 類型:
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 接口 來創建自己的模塊。 在下一部分,我們將看到如何使用一些 內置模塊。 我們將探索 Jackson 和 Jakarta 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。
生成的模式已正確設置 required 和 readOnly 字段:
{
"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 目標的一部分創建出來。