Json架构带有anyOf字段的POJO

huangapple go评论78阅读模式
英文:

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&lt;String, Object&gt; hobby;

or List<Map<String, Object> hobbies> if one can have multiple hobbies.

答案2

得分: 0

以下是翻译好的部分:

我的最终方法是使用Jackson提供的多态编组/解组功能。

具体来说 -

  1. 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 {
}
  1. 创建 Exercise.javaMusic.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;
    //... (其他字段)
}
  1. 使用 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 -

  1. Make hobby to be an interface and annotate it with @JsonTypeInfo and @JsonSubTypes
@JsonTypeInfo(
    use = JsonTypeInfo.Id.NAME,
    property = &quot;hobbyType&quot;,
    include = JsonTypeInfo.As.EXISTING_PROPERTY,
    visible = true
)
@JsonSubTypes({
        @Type(value = Exercise.class, name = &quot;exercise&quot;),
        @Type(value = Music.class, name = &quot;music&quot;)
})
public interface Hobby {
}
  1. Create Exercise.java and Music.java that implements this interface
@Builder
@Data
@AllArgsConstructor
public class Exercise implements Hobby {
    @JsonProperty(&quot;hobbyType&quot;)
    @Builder.Default
    @NonNull
    private final String hobbyType = &quot;exercise&quot;;    

    @JsonProperty(&quot;exerciseName&quot;)
    private String exerciseName;

    @JsonProperty(&quot;place&quot;)
    private String place;
    //... (other fields)
}
  1. Use Hobby for serialization and deserialization.
// create a Hobby object
Hobby exercise = Exercise.builder().exerciseName(&quot;swimming&quot;).place(&quot;swimmingPool&quot;).build();

// serialization
String serializedHobby = new ObjectMapper.writeValueAsString(exercise)
/**
serializedHobby looks like this -&gt;
{
    &quot;hobbyType&quot;: &quot;exercise&quot;,
    &quot;exerciseName&quot;: &quot;swimming&quot;,
    &quot;place&quot;: &quot;swimmingPool&quot;
}
*/

// deserialization
Hobby deserializedObject = new ObjectMapper.readValue(jsonString, Hobby.class)
// deserializedObject.getClass() would return Exercise.java or Music.java based on the hobbyType

Ref: https://www.baeldung.com/jackson-inheritance

huangapple
  • 本文由 发表于 2020年8月9日 04:07:07
  • 转载请务必保留本文链接:https://go.coder-hub.com/63319783.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定