Java Jackson 序列化摘要列表 Class

Java Jackson Serialize List of Abstract Class

场景

想象一下,我有一个名为 InfoItem 的抽象 class,从它继承的每个 class 都是一种不同类型的信息类型,稍后用于帮助我处理class化.

每个InfoItem都有一个timestamp作为一个属性(这个InfoItem什么时候到)?

注意:我在这个例子中使用Lombok

@Data
@JsonTypeInfo(use = Id.NAME, visible = true, property = "@type")
public abstract class InfoItem {
    private LocalDateTime timestamp;
}

我有 2 个 class 扩展这个 class:AlphaBeta。 每个 class 都有多个属性,它们之间没有共同的 属性:

@JsonTypeName("alpha")
@Data
public class Alpha extends InfoItem {
    private int x;
    private long y;
}

@JsonTypeName("beta")
@Data
public class Beta extends InfoItem {
    private bool isOk;
}

现在假设我创建了一个包含 InfoItem 项的列表:

list[0] = Alpha(x = 10, y = 20)
list[1] = Beta(isOk = false)

问题

如果上面的列表被保存为一个名为 lst 的变量,那么我会 运行 这个命令:

ObjectMapper mapper = new ObjectMapper();
String s = mapper.writeValueAsString(lst);
System.out.println(s);

我会得到这个:

[
    {
        "x": 10,
        "y": 20
    },
    {
        "isOk": false
    }
]

而不是:

[
    {
        "@type": "alpha",
        "x": 10,
        "y": 20
    },
    {
        "@type": "beta",
        "isOk": false
    }
]

当我 运行 直接在 ListObjectMapper 而不是我创建并放入列表的 class 时,就会发生这种情况。

案例:列表在我创建的class

如果我创建一个名为 FooBar 的 class,它有一个 属性 lst(类型 List` - 同上):

@Data
public class FooBar {
    private List<InfoItem> items;
}

然后我做:

FooBar bar = new FooBar();
bar.setItems(lst);

ObjectMapper mapper = new ObjectMapper();
String s = mapper.writeValueAsString(lst);
System.out.println(s);

我会得到:

{
    "items": [
        {
            "@type": "alpha",
            "x": 10,
            "y": 20
        },
        {
            "@type": "beta",
            "isOk": false
        }
    ]
}

如果你用这样的东西注释你的 abstract class 就可以做到这一点(你可能需要根据你的需要调整它):

@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "_type")
@JsonSubTypes({
  @JsonSubTypes.Type(value = Alpha.class, name = "_type"),
  @JsonSubTypes.Type(value = Beta.class, name = "_type"),
})

当您生成 JSON 时,将插入一个 _type 键,其值为 AlphaBeta 等的 FQDN class。对于您的用例,它的工作方式应该类似。

查看 是否有助于了解更多背景知识。前一段时间我在做类似的事情——不过我还没有更新问题:)

几天后我找到了解决方案:

public static <T> Optional<String> toJson(final ObjectMapper mapper, final TypeReference<T> type, final T object) {
    try {
        return Optional.ofNullable(mapper.writer().forType(type).writeValueAsString(object));
    } catch (JsonProcessingException e) {
        return Optional.empty();
    }
}

public static void main(String[] args) {
    List<InfoItem> items = new ArrayList<>();
    // Set values in items...

    ObjectMapper mapper = new ObjectMapper();
    Optional<String> json = toJson(mapper, new TypeReference<List<InfoItem>>() {}, items);
    json.ifPresent(x -> System.out.println(x));
}