Java Джексон сериализует список абстрактных классов - PullRequest
0 голосов
/ 22 декабря 2018

Сценарий

Представьте, что у меня есть абстрактный класс с именем InfoItem, что каждый класс, который наследуется от него, представляет собой отдельный тип информационного типа, который позже используется, чтобы помочь мне с классификацией.

Каждый InfoItem имеет timestamp в качестве свойства (когда наступает этот InfoItem)?

Примечание : я использую Ломбок в этом примере

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

У меня есть 2 класса, которые расширяют этот класс: Alpha иBeta.У каждого класса есть несколько свойств, которые не имеют общего свойства между ними:

@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)

Issue

Если вышеприведенный список будет сохранен как переменная с именем 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
    }
]

Это происходит, когда я запускаю ObjectMapper над List напрямую, а не классом, который я создаю и помещаю в него список.

Case: Список в классе, который я создал

Если я создаю класс с именем FooBar, обладающий свойством 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
        }
    ]
}

Ответы [ 2 ]

0 голосов
/ 26 декабря 2018

Я нашел через несколько дней решение:

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));
}
0 голосов
/ 22 декабря 2018

Это можно сделать, если вы аннотируете свой класс abstract чем-то вроде этого (вам может понадобиться настроить его под свои нужды):

@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, который вставит *Клавиша 1005 * со значением класса FQDN для Alpha, Beta и т. Д. Она должна работать аналогично вашему случаю использования.

Посмотрите, поможет ли эта для дополнительной информации,Я делал нечто подобное некоторое время назад - хотя я не обновил вопрос:)

...