英文:
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类?
英文:
I wonder what's the recommended way to generate POJOs for Json schemas with "anyOf" fields?
For example, given the following json schemas:
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" }
}
}
How could I generate a POJO for Hobby.java
with Jackson?
答案1
得分: 0
我认为有两种自然的方法:
一种是生成一个类层次结构Hobby,其中包含常见字段timeSpent,而Music / Exercise是它们的子类,具有它们特定的字段。
另一种方法是将这些字段“合并”到一个单独的Hobby类中。
这两种方法在语义上都是不正确的,这意味着您可以找到JSON模式验证正确但Jackson引发错误或由于省略字段而在POJO中缺少信息的情况。
所以我认为在这里最好的方法是改为使用Map<String, Object>而不是pojos。
因此,例如,如果一个人有一项爱好,Person POJO可以如下所示:
class Person {
String name;
...
Map<String, Object> hobby;
}
或者如果一个人可以有多个爱好,则可以使用List<Map<String, Object>> hobbies。
英文:
I think there are two approaches that seem natural:
One would be to generate a class hierarchy Hobby with the common field timeSpent and Music / Exercise being subclasses with their specific fields.
The other would be to "union" the fields into a single class Hobby.
Both are semantically incorrect meaning that you can come up with cases where JSON schema validates correctly but Jackson throws an error or the information is missing in the POJO due to an omitted field.
So I think the best approach here would be to resort to Map<String, Object> instead of pojos.
So for example if a Person has a hobby the Person POJO could be:
class Person {
String name;
...
Map<String, Object> hobby;
or List<Map<String, Object> hobbies> if one can have multiple hobbies.
答案2
得分: 0
以下是翻译好的部分:
我的最终方法是使用Jackson提供的多态编组/解组功能。
具体来说 -
- 将
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;
//... (其他字段)
}
- 使用
Hobby
进行序列化和反序列化。
// 创建一个 Hobby 对象
Hobby exercise = Exercise.builder().exerciseName("swimming").place("swimmingPool").build();
// 序列化
String serializedHobby = new ObjectMapper.writeValueAsString(exercise)
/**
serializedHobby 看起来像这样 ->
{
"hobbyType": "exercise",
"exerciseName": "swimming",
"place": "swimmingPool"
}
*/
// 反序列化
Hobby deserializedObject = new ObjectMapper.readValue(jsonString, Hobby.class)
// deserializedObject.getClass() 会根据 hobbyType 返回 Exercise.java 或 Music.java
Ref: https://www.baeldung.com/jackson-inheritance
英文:
The approach I ended up taking is using polymorphic marshaling/unmarshaling functionality provided by Jackson.
Specifically -
- Make
hobby
to be an interface and annotate it with@JsonTypeInfo
and@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 {
}
- Create
Exercise.java
andMusic.java
that implements this interface
@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)
}
- Use
Hobby
for serialization and deserialization.
// 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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论