Json 包含任何字段到 POJO 的模式
Json schema with anyOf fields to POJO
我想知道为具有“anyOf”字段的 Json 模式生成 POJO 的推荐方法是什么?
例如,给定以下 json 模式:
hobby.json
{
"anyOf": [
{ "type": {"$ref": "./exercise.json" } },
{ "type": {"$ref": "./music.json" } }
]
}
exercise.json
{
"type": "object"
"properties" {
"hobbyType": {"type": "string"}
"exerciseName": { "type": "string" },
"timeSpent": { "type": "number" },
"place": { "type": "string" }
}
}
music.json
{
"type": "object"
"properties" {
"hobbyType": {"type": "string"}
"instrument": { "type": "string" },
"timeSpent": { "type": "number" }
}
}
如何使用 Jackson 为 Hobby.java
生成 POJO?
我认为有两种方法看起来很自然:
一种方法是生成一个 class 层次结构 Hobby,其公共字段 timeSpent 和 Music / Exercise 是其特定字段的子classes。
另一种方法是将这些字段“联合”成一个 class 爱好。
两者在语义上都不正确,这意味着您可能会遇到 JSON 模式验证正确但 Jackson 抛出错误或 POJO 中由于省略字段而丢失信息的情况。
所以我认为最好的方法是求助于 Map 而不是 pojos。
例如,如果一个人有一个爱好,那么这个人的 POJO 可能是:
class Person {
String name;
...
Map<String, Object> hobby;
或 List
我最终采用的方法是使用 Jackson 提供的多态 marshaling/unmarshaling 功能。
具体来说-
- 将
hobby
设为接口,并用@JsonTypeInfo
和@JsonSubTypes
注解
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
property = "hobbyType",
include = JsonTypeInfo.As.EXISTING_PROPERTY,
visible = true
)
@JsonSubTypes({
@Type(value = Exercise.class, name = "exercise"),
@Type(value = Music.class, name = "music")
})
public interface Hobby {
}
- 创建实现此接口的
Exercise.java
和Music.java
@Builder
@Data
@AllArgsConstructor
public class Exercise implements Hobby {
@JsonProperty("hobbyType")
@Builder.Default
@NonNull
private final String hobbyType = "exercise";
@JsonProperty("exerciseName")
private String exerciseName;
@JsonProperty("place")
private String place;
//... (other fields)
}
- 使用
Hobby
进行序列化和反序列化。
// create a Hobby object
Hobby exercise = Exercise.builder().exerciseName("swimming").place("swimmingPool").build();
// serialization
String serializedHobby = new ObjectMapper.writeValueAsString(exercise)
/**
serializedHobby looks like this ->
{
"hobbyType": "exercise",
"exerciseName": "swimming",
"place": "swimmingPool"
}
*/
// deserialization
Hobby deserializedObject = new ObjectMapper.readValue(jsonString, Hobby.class)
// deserializedObject.getClass() would return Exercise.java or Music.java based on the hobbyType
我想知道为具有“anyOf”字段的 Json 模式生成 POJO 的推荐方法是什么?
例如,给定以下 json 模式:
hobby.json{
"anyOf": [
{ "type": {"$ref": "./exercise.json" } },
{ "type": {"$ref": "./music.json" } }
]
}
exercise.json
{
"type": "object"
"properties" {
"hobbyType": {"type": "string"}
"exerciseName": { "type": "string" },
"timeSpent": { "type": "number" },
"place": { "type": "string" }
}
}
music.json
{
"type": "object"
"properties" {
"hobbyType": {"type": "string"}
"instrument": { "type": "string" },
"timeSpent": { "type": "number" }
}
}
如何使用 Jackson 为 Hobby.java
生成 POJO?
我认为有两种方法看起来很自然:
一种方法是生成一个 class 层次结构 Hobby,其公共字段 timeSpent 和 Music / Exercise 是其特定字段的子classes。
另一种方法是将这些字段“联合”成一个 class 爱好。
两者在语义上都不正确,这意味着您可能会遇到 JSON 模式验证正确但 Jackson 抛出错误或 POJO 中由于省略字段而丢失信息的情况。
所以我认为最好的方法是求助于 Map
例如,如果一个人有一个爱好,那么这个人的 POJO 可能是:
class Person {
String name;
...
Map<String, Object> hobby;
或 List
我最终采用的方法是使用 Jackson 提供的多态 marshaling/unmarshaling 功能。
具体来说-
- 将
hobby
设为接口,并用@JsonTypeInfo
和@JsonSubTypes
注解
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
property = "hobbyType",
include = JsonTypeInfo.As.EXISTING_PROPERTY,
visible = true
)
@JsonSubTypes({
@Type(value = Exercise.class, name = "exercise"),
@Type(value = Music.class, name = "music")
})
public interface Hobby {
}
- 创建实现此接口的
Exercise.java
和Music.java
@Builder
@Data
@AllArgsConstructor
public class Exercise implements Hobby {
@JsonProperty("hobbyType")
@Builder.Default
@NonNull
private final String hobbyType = "exercise";
@JsonProperty("exerciseName")
private String exerciseName;
@JsonProperty("place")
private String place;
//... (other fields)
}
- 使用
Hobby
进行序列化和反序列化。
// create a Hobby object
Hobby exercise = Exercise.builder().exerciseName("swimming").place("swimmingPool").build();
// serialization
String serializedHobby = new ObjectMapper.writeValueAsString(exercise)
/**
serializedHobby looks like this ->
{
"hobbyType": "exercise",
"exerciseName": "swimming",
"place": "swimmingPool"
}
*/
// deserialization
Hobby deserializedObject = new ObjectMapper.readValue(jsonString, Hobby.class)
// deserializedObject.getClass() would return Exercise.java or Music.java based on the hobbyType