Как распарсить полиморфный объект Jackson по наличию полей?

Из-за недостаточной эффективности Gson приходится переходить на что-то другое, и сейчас выбор пал на Jackson.
Как мне распарсить объекты одного массива?
От сервера приходит json:

{
    documents: [
        {
            "id" : 1,
            "number" : "12345",
            "assure" : true
            ...
        },
        ........
        {
            "id" : 5,
            "number" : "123456",
            "photo" : "https://link.com"
            ...
        }
    ]
}

мне нужно их положить в такую структуру:

public abstract class Document{
    long id;
    String number;
}

public class Passport extends Document{
    boolean assure;
}

public class Contract extends Document{
    String photo;
}

В примерах в интернете показывают, что нужно создать ключевое поле и определять по его значению тип объекта. У меня api недоступно для изменения, и нужно парсить в зависимости от наличия того или иного поля. В Jackson'е 2 дня.
Буду благодарен за помощь и пояснение


Ответы (2 шт):

Автор решения: ЮрийСПб

Судя по этой ссылке должно быть как-то так:

@JsonDeserialize(using = PropertyPresentDeserializer.class)
@JsonSubTypes({
        @Type(name = "assure", value = Passport .class),
        @Type(name = "photo", value = Contract .class)
})
public abstract class Document{
    long id;
    String number;
}

public class Passport extends Document{
    boolean assure;
}

public class Contract extends Document{
    String photo;
}
→ Ссылка
Автор решения: Дмитрий

А вы не думали, над тем, чтобы сделать так:

import com.google.gson.Gson;
import java.util.List;
import java.util.stream.Collectors;

@lombok.Data
public class DocumentParser {

    private List<DocumentJson> documents;

    private final static Gson GSON = new Gson();
    
    public static List<Document> parse(String json) {
        return GSON.fromJson(json, DocumentParser.class).documents.stream()
                .map(DocumentParser::getInstance)
                .collect(Collectors.toList());
    }

    @lombok.Data
    private static class DocumentJson {
        private Long id;
        private String number;        
        private Boolean assure;
        private String photo;
    }
    
    //этот код может быть перенесен и реализован как паттерн фабричный метод
    //в любом случае вам необходимо будет каким-то образом создавать экземпляры данных классов
    private static Document getInstance(DocumentJson documentJson) {
        
        if ((documentJson.getAssure()==null && documentJson.getPhoto()==null) || 
                (documentJson.getAssure()!=null && documentJson.getPhoto()!=null)) 
            throw new RuntimeException("Not supporetd...");
        
        if (documentJson.getAssure()==null) return Contract.builder()
                    .id(documentJson.getId())
                    .number(documentJson.getNumber())
                    .photo(documentJson.getPhoto())
                    .build();
        else return Passport.builder()
                    .id(documentJson.getId())
                    .number(documentJson.getNumber())
                    .assure(documentJson.getAssure())
                    .build();
    }
    
}

@lombok.Data
@lombok.NoArgsConstructor
@lombok.AllArgsConstructor
public abstract class Document {

    private Long id;
    private String number;

}

@lombok.Data
@lombok.NoArgsConstructor
@lombok.EqualsAndHashCode(callSuper = true)
@lombok.ToString(callSuper = true)
public class Passport extends Document{
    
    private Boolean assure;
    
    @lombok.Builder
    public Passport(Long id, String number, Boolean assure) {
        super(id, number);
        this.assure = assure;
    }
    
}

@lombok.Data
@lombok.NoArgsConstructor
@lombok.EqualsAndHashCode(callSuper = true)
@lombok.ToString(callSuper = true)
public class Contract extends Document{
    
    private String photo;
    
    @lombok.Builder
    public Contract(Long id, String number, String photo) {
        super(id, number);
        this.photo = photo;
    }
    
}

Все дело в том, что у вас нет идентификатора, по которому можно определить, какой именно инстанс нужно создавать. Это значит, что вам нужно залезть в джейсон и создавать их по определенному условию. При таких исходных данных, как по мне , лучше написать логику на джаве. Она достаточно несложная. И это в любом случае проще и более читаемо, чем пытаться сконфигурировать Jackson либо любую другую библиотеку. мало того, как уже написано в комментариях, из метода getInstance достаточно легко сделать паттерн фабричный метод и использовать его для инстанциирования экземпляров классов данной иерархии в других случаях их использования. Это достаточно просто расширяется в случае добавления дополнительных наследников, а также добавляется другая логика инстанциирования. При этом код остается читаемым.

→ Ссылка