wake-up-neo.net

Jackson JSON-Bibliothek: So instanziieren Sie eine Klasse, die abstrakte Felder enthält

Ich möchte einen JSON-String in ein Java-Objekt konvertieren, aber die Klasse dieses Objekts enthält abstrakte Felder, die Jackson nicht instanziieren kann und das Objekt nicht erzeugt. Was ist der einfachste Weg, um etwas über eine Standardimplementierung einer abstrakten Klasse zu erfahren, wie

setDefault(AbstractAnimal.class, Cat.class);

oder um über die Implementierungsklasse basierend auf dem JSON-Attributnamen zu entscheiden, z. für JSON-Objekt: 

{
    ...
    cat: {...}
    ...
}

ich würde nur witze: 

setImpl("cat", Cat.class);


Ich weiß, dass es in Jackson möglich ist, Klasseninformationen in JSON einzubetten, aber ich möchte das von mir verwendete JSON-Format nicht verkomplizieren. Ich möchte entscheiden, welche Klasse verwendet werden soll, indem Sie die Standardimplementierungsklasse festlegen oder den Attributnamen ('cat') festlegen - wie in der XStream-Bibliothek. Dort schreiben Sie:

xStream.alias("cat", Cat.class);

Gibt es eine Möglichkeit, dies insbesondere in einer Zeile, oder erfordert es mehr Code?

27
Marcin

Es gibt mehrere Möglichkeiten. vor Version 1.8 ist der einfachste Weg wahrscheinlich zu tun:

@JsonDeserialize(as=Cat.class)
public abstract class AbstractAnimal { ... }

um auf der Grundlage von Attributen zu entscheiden, geschieht dies am besten mit @JsonTypeInfo, das die automatische Einbettung (beim Schreiben) und die Verwendung von Typinformationen durchführt.

Es gibt mehrere Arten von Typinformationen (Klassenname, logischer Typname) sowie Einschlussmechanismen (as-included-property, as-wrapper-array, as-wrapper-object). Auf dieser Seite: https://github.com/FasterXML/jackson-docs/wiki/JacksonPolymorphicDeserialization werden einige Konzepte erläutert.

38
StaxMan

Eine vollständige Antwort mit einem sehr klaren Beispiel finden Sie hier: https://stackoverflow.com/a/30386694/584947

Jackson bezeichnet dies als polymorphe Deserialisierung.

Es hat mir definitiv bei meinem Problem geholfen. Ich hatte eine abstrakte Klasse, die ich in einer Datenbank speicherte, und musste sie (verständlicherweise) in eine konkrete Instanz einer Klasse dekomprimieren.

Es zeigt Ihnen, wie Sie die übergeordnete abstrakte Klasse richtig annotieren und wie Sie Jackson beibringen, wie Sie zur Laufzeit unter den verfügbaren Unterklassenkandidaten auswählen können, wenn Sie das Marshallen starten.

2
anon58192932

Wenn Sie weder Ihr JSON mit zusätzlichen Feldern noch Ihre Klassen mit Annotationen verschmutzen möchten, können Sie ein sehr einfaches Modul und einen Deserializer schreiben, der die von Ihnen gewünschte Standard-Unterklasse verwendet. Es gibt mehr als eine Zeile aufgrund einiger Code-Code, aber es ist immer noch relativ einfach.

class AnimalDeserializer extends StdDeserializer<Animal> {
    public AnimalDeserializer() {
        super(Animal.class);
    }

    public Animal deserialize(JsonParser jsonParser, DeserializationContext context) throws IOException {
        return jsonParser.readValueAs(Cat.class);
    }
}

class AnimalModule extends SimpleModule {
    {
        addDeserializer(Animal.class, new AnimalDeserializer());
    }
}

Dann registrieren Sie dieses Modul für den ObjectMapper und das ist es (Zoo ist die Containerklasse, die ein Animal-Feld besitzt).

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new AnimalModule());
return objectMapper.readValue(json, Zoo.class);
0
István

das Problem kann mit der Anmerkung @JsonDeserialize für die abstrakte Klasse gelöst werden .__ bezieht sich auf Jackson Exceptions Problems and Solutions https://www.baeldung.com/jackson-exception

0
zedtimi