Подтвердить что ты не робот

Полиморфическая десериализация Джексона. Можете ли вы потребовать существование поля вместо определенного значения?

Использование спина на примере зоопарка:

public class ZooPen {
    public String type;
    public List<Animal> animals;
}

public class Animal {
    public String name;
    public int age;
}

public class Bird extends Animal {
    public double wingspan;
}

Я хочу использовать полиморфную десериализацию для создания экземпляров Animal, если не указано размах крыльев, и экземпляры Bird, если они есть. В Джексоне нетипизированная десериализация обычно выглядит примерно так:

@JsonTypeInfo( 
    use = JsonTypeInfo.Id.NAME,
    include = JsonTypeInfo.As.PROPERTY,
    property = "wingspan",
    visible = true,
    defaultImpl = Animal.class
)
@JsonSubTypes({
    @Type(value = Bird.class, name = **???**)
})  
public class Animal {
    ...
}

Значение wingspan может быть любым, и без этого что-то конкретное, Джексон возвращается к классу defaultImpl.

Возможно, я использовал @JsonCreator

@JsonCreator
public static Animal create(Map<String,Object> jsonMap) 
        throws JsonProcessingException {

    ObjectMapper mapper = new ObjectMapper();
    if (jsonMap.get("wingspan") == null) {
        // Construct and return animal
    } else {
        // Construct and return bird
    }
}

Однако тогда мне приходится вручную обрабатывать дополнительные значения и вызывать непротиворечивые исключения, и неясно, будет ли сериализоваться позднее.

Есть ли способ сделать то, что я хочу, с обработкой полиморфного типа? Кажется, я могу использовать свой собственный TypeResolver или TypeIdResolver, но это похоже на большую работу, чем просто десериализация самого сырого json. В качестве альтернативы, может быть способ нацеливаться на тип руля-зоопарка, даже если он находится в родительском объекте. Любые идеи?

Edit:
TypeResolver и TypeIdResolver, по-видимому, предполагают, что информация о типе сериализована, поэтому они не подходят для использования. Было бы возможно реализовать мой собственный JsonDeserializer, который перехватывает жизненный цикл, чтобы указать тип, но все же использует основные функции обработки аннотаций jackson? Я смотрю на JsonDeserializer.deserializeWithType(...), но, похоже, полностью делегирует десериализацию к TypeDeserializer. Там также проблема, что мне нужно будет десериализовать часть объекта, прежде чем я узнаю, какой тип использовать.

4b9b3361

Ответ 1

Если вы не состоите в браке с Джексоном, я считаю, что нечто подобное этому можно выполнить с помощью FlexJSON.

http://flexjson.sourceforge.net/javadoc/flexjson/JSONDeserializer.html

Я не знаком с методами Джексона для подобных действий, но могу сказать, что FlexJSON очень эффективен и в целом интуитивно понятен для использования во время шагов сериализации/десериализации.

Ответ 2

Не отвечая непосредственно на ваш вопрос, я подумал, что стоит отметить, что использование @JsonCreator не слишком обременительно:

@JsonCreator
public static Animal create(Map<String,Object> jsonMap) {
    String name = (String) jsonMap.get("name");
    int age = (int) jsonMap.get("age");
    if (jsonMap.keySet().contains("wingspan")) {
        double wingspan = (double) jsonMap.get("wingspan");
        return new Bird(name, age, wingspan);
    } else {
        return new Animal(name, age);
    }
}

Не нужно бросать JsonProcessingException. Этот пользовательский десериализатор выйдет из строя по тем же причинам, что и встроенный десериализатор Джексона, а именно, приведение исключений. Для сложной десериализации я предпочитаю такой способ работы, так как он делает код намного проще для понимания и изменения.