it-swarm.com.de

Deserialisierung von JSON mit Jackson in polymorphe Typen - Ein vollständiges Beispiel führt zu einem Kompilierungsfehler

Ich versuche, ein Tutorial von Programmer Bruce durchzuarbeiten, das die Deserialisierung von polymorphem JSON ermöglichen soll. 

Die vollständige Liste finden Sie hier Programmer Bruce Tutorials (Tolles Zeug übrigens)

Ich habe die ersten fünf ohne Probleme durchgearbeitet, aber beim letzten habe ich einen Haken bekommen (Beispiel 6).

Beim Kompilieren wird folgende Fehlermeldung angezeigt

Die Methode readValue (JsonParser, Class) vom Typ ObjectMapper ist für die Argumente (ObjectNode, Class) nicht anwendbar.

und es wird durch den Codeabschnitt verursacht

  public Animal deserialize(  
      JsonParser jp, DeserializationContext ctxt)   
      throws IOException, JsonProcessingException  
  {  
    ObjectMapper mapper = (ObjectMapper) jp.getCodec();  
    ObjectNode root = (ObjectNode) mapper.readTree(jp);  
    Class<? extends Animal> animalClass = null;  
    Iterator<Entry<String, JsonNode>> elementsIterator =   
        root.getFields();  
    while (elementsIterator.hasNext())  
    {  
      Entry<String, JsonNode> element=elementsIterator.next();  
      String name = element.getKey();  
      if (registry.containsKey(name))  
      {  
        animalClass = registry.get(name);  
        break;  
      }  
    }  
    if (animalClass == null) return null;  
    return mapper.readValue(root, animalClass);
  }  
} 

Insbesondere durch die Linie 

return mapper.readValue (root, animalClass);

Hat jemand das schon mal gemacht und wenn ja, gab es eine Lösung?

Ich würde mich über jede Hilfe freuen, die jeder geben kann. Vielen Dank im Voraus. Jon.

47
Jon Driscoll

Wie versprochen gebe ich ein Beispiel für die Verwendung von Annotationen zum Serialisieren/Deserialisieren von polymorphen Objekten. Ich habe dieses Beispiel in der Animal-Klasse des Tutorials, das Sie gerade gelesen haben, verwendet.

Zunächst Ihre Animal-Klasse mit den Json-Annotationen für die Unterklassen.

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;

@JsonIgnoreProperties(ignoreUnknown = true)
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY)
@JsonSubTypes({
    @JsonSubTypes.Type(value = Dog.class, name = "Dog"),

    @JsonSubTypes.Type(value = Cat.class, name = "Cat") }
)
public abstract class Animal {

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

Dann Ihre Unterklassen Dog und Cat.

public class Dog extends Animal {

    private String breed;

    public Dog() {

    }

    public Dog(String name, String breed) {
        setName(name);
        setBreed(breed);
    }

    public String getBreed() {
        return breed;
    }

    public void setBreed(String breed) {
        this.breed = breed;
    }
}

public class Cat extends Animal {

    public String getFavoriteToy() {
        return favoriteToy;
    }

    public Cat() {}

    public Cat(String name, String favoriteToy) {
        setName(name);
        setFavoriteToy(favoriteToy);
    }

    public void setFavoriteToy(String favoriteToy) {
        this.favoriteToy = favoriteToy;
    }

    private String favoriteToy;

}

Wie Sie sehen, gibt es für Cat und Dog nichts Besonderes. Die einzige, die über sie Bescheid weiß, ist die abstract-Klasse Animal. Wenn Sie deserialisieren, zielen Sie auf Animal und die ObjectMapper gibt die tatsächliche Instanz zurück, wie Sie sie sehen können der folgende Test:

public class Test {

    public static void main(String[] args) {

        ObjectMapper objectMapper = new ObjectMapper();

        Animal myDog = new Dog("ruffus","english shepherd");

        Animal myCat = new Cat("goya", "mice");

        try {
            String dogJson = objectMapper.writeValueAsString(myDog);

            System.out.println(dogJson);

            Animal deserializedDog = objectMapper.readValue(dogJson, Animal.class);

            System.out.println("Deserialized dogJson Class: " + deserializedDog.getClass().getSimpleName());

            String catJson = objectMapper.writeValueAsString(myCat);

            Animal deseriliazedCat = objectMapper.readValue(catJson, Animal.class);

            System.out.println("Deserialized catJson Class: " + deseriliazedCat.getClass().getSimpleName());



        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

Ausgabe nach dem Ausführen der Test-Klasse:

{"@type":"Dog","name":"ruffus","breed":"english shepherd"}

Deserialized dogJson Class: Dog

{"@type":"Cat","name":"goya","favoriteToy":"mice"}

Deserialized catJson Class: Cat

Hoffe das hilft,

Jose Luis

90
jbarrueta

Eine einfache Möglichkeit, polymorphe Serialisierung/Deserialisierung über die Jackson-Bibliothek zu aktivieren, besteht darin, den Jackson-Objekt-Mapper (jackson.databind.ObjectMapper) global zu konfigurieren, um Informationen für bestimmte Klassen wie abstrakte Klassen hinzuzufügen, beispielsweise den konkreten Klassentyp. 

Stellen Sie dazu sicher, dass Ihr Mapper korrekt konfiguriert ist. Zum Beispiel:

Option 1: Unterstützung der polymorphen Serialisierung/Deserialisierung für abstrakte Klassen (und Klassen mit Objekttyp)

jacksonObjectMapper.enableDefaultTyping(
    ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE); 

Option 2: Unterstützung der polymorphen Serialisierung/Deserialisierung für abstrakte Klassen (und Klassen mit Objekttyp) und Arrays dieser Typen.

jacksonObjectMapper.enableDefaultTyping(
    ObjectMapper.DefaultTyping.NON_CONCRETE_AND_ARRAYS); 

Referenz: https://github.com/FasterXML/jackson-docs/wiki/JacksonPolymorphicDeserialization

1
AmitW

Wenn Sie die quickerxml verwenden,

diese Änderungen sind möglicherweise erforderlich

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.node.ObjectNode;

in main method--

verwenden

SimpleModule module =
  new SimpleModule("PolymorphicAnimalDeserializerModule");

anstatt von

new SimpleModule("PolymorphicAnimalDeserializerModule",
      new Version(1, 0, 0, null));

nehmen Sie in der Funktion Animal deserialize () die folgenden Änderungen vor

//Iterator<Entry<String, JsonNode>> elementsIterator =  root.getFields();
Iterator<Entry<String, JsonNode>> elementsIterator = root.fields();

//return mapper.readValue(root, animalClass);
return  mapper.convertValue(root, animalClass); 

Dies funktioniert für "fasterxml.jackson". Wenn es sich immer noch über die Klassenfelder beschwert. Verwenden Sie für die Feldnamen dasselbe Format wie im json (mit "_" -Unterstrich). wie das
//mapper.setPropertyNamingStrategy(new CamelCaseNamingStrategy()); wird möglicherweise nicht unterstützt.

abstract class Animal
{
  public String name;
}

class Dog extends Animal
{
  public String breed;
  public String leash_color;
}

class Cat extends Animal
{
  public String favorite_toy;
}

class Bird extends Animal
{
  public String wing_span;
  public String preferred_food;
}
0
ravi.zombie

Sie benötigen nur eine Zeile vor der Deklaration der Klasse Animal für die korrekte polymorphe Serialisierung/Deserialisierung:

@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class")
public abstract class Animal {
   ...
}

Diese Zeile bedeutet: Hinzufügen einer Meta-Eigenschaft bei der Serialisierung oder Lesen einer Meta-Eigenschaft bei der Deserialisierung (include = JsonTypeInfo.As.PROPERTY) mit dem Namen "@class" (property = "@class"), die das vollqualifizierte Javaenthält. _ Klassenname (use = JsonTypeInfo.Id.CLASS).

Wenn Sie also direkt (ohne Serialisierung) eine JSON erstellen, müssen Sie die Meta-Eigenschaft "@class" mit dem gewünschten Klassennamen für die korrekte Deserialisierung hinzufügen.

Weitere Informationen hier

0
Marco