2015-05-21 18 views
30

Sto tentando di elaborare un tutorial del programmatore Bruce che dovrebbe consentire la deserializzazione di JSON polimorfo.Deserializzare JSON con Jackson in tipi polimorfici - Un esempio completo mi sta dando un errore di compilazione

L'elenco completo può essere trovato qui Programmer Bruce tutorials (roba Grande btw)

Ho lavorato attraverso i primi cinque senza problemi, ma ho colpito un intoppo sulla ultimo (esempio 6), che naturalmente è quello che ho davvero bisogno di lavorare.

sto ottenendo il seguente errore in fase di compilazione

Il metodo readValue (JsonParser, Class) nel tipo ObjectMapper non è applicabile per gli argomenti (ObjectNode, Class)

ed è essendo causato dal pezzo di codice

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); 
    } 
} 

particolare dalla linea

return mapper.readValue (root, animalClass);

Qualcuno si è imbattuto in questo prima e se sì, c'era una soluzione?

Apprezzerei tutto l'aiuto che chiunque può dare Grazie in anticipo Jon D.

+0

Quale versione stai usando, l'esercitazione si assume Jackson 1.x, anche qualche ragione per cui non preferire le annotazioni basata deserializzazione per le istanze polimorfici Jackson? – jbarrueta

+0

Sto usando 2.5. Posso vedere se un downgrade a 1.X risolverà il problema. Inoltre, puoi consigliare un tutorial/esempio che potrebbe mostrare l'uso di annotazioni per gestire questo problema? –

+0

Sì, non ti consiglierei di effettuare il downgrade, darò un esempio funzionante. – jbarrueta

risposta

62

Come promesso, sto mettendo un esempio su come utilizzare le annotazioni per serializzare/deserializzare oggetti polimorfici, ho basato questo esempio nella classe Animal del tutorial che stavi leggendo.

Prima di tutto la classe Animal con Annotazioni Json per le sottoclassi.

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; 
    } 

} 

Allora la vostra sottoclassi, Dog e 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; 

} 

Come si può vedere, non c'è niente di speciale per Cat e Dog, l'unico che sa su di loro è il abstract classe Animal, in modo che quando la deserializzazione, ti bersaglio per Animal e la ObjectMapper restituirà il esempio reale come si può vedere nella seguente 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(); 
     } 

    } 
} 

uscita dopo l'esecuzione della classe Test:

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

Deserialized dogJson Class: Dog

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

Deserialized catJson Class: Cat

Spero che questo aiuti,

Jose Luis

+2

Cosa succede se ottengo l'oggetto da Retrofit senza le informazioni @type? –

+3

È possibile farlo senza utilizzare l'annotazione @JsonSubTypes? In modo che le sottoclassi possano essere aggiunte in altri pacchetti dopo che la classe originale è stata creata? – HDave

+0

Sai se questo potrebbe funzionare se i sottotipi sono in diversi progetti Maven? i.e: Cat, Dog sono ciascuno all'interno di un diverso progetto Maven rispetto a Animal. Ho un problema con la dipendenza circolare e non riesco a capire come uscirne. – cyberjoac

0

Se si utilizza il fasterxml poi,

potrebbero essere necessari

questi cambiamenti

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 method-- principale

uso

SimpleModule module = 
    new SimpleModule("PolymorphicAnimalDeserializerModule"); 

anziché

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

in deserialize animale() funzione, fanno sotto modifiche

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

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

Questo funziona per fasterxml.jackson. Se si lamenta ancora dei campi di classe. Usa lo stesso formato di JSON per i nomi dei campi (con "_" -underscore). poiché questo
//mapper.setPropertyNamingStrategy(new CamelCaseNamingStrategy()); potrebbe non essere supportato.

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; 
} 
1

Un modo semplice per consentire polimorfico serializzazione/deserializzazione tramite Jackson biblioteca è globalmente configurare l'oggetto mapper Jackson (jackson.databind.ObjectMapper) per aggiungere informazioni, come ad esempio il tipo di classe di cemento, per alcuni tipi di classi , come lezioni astratte.

Per fare ciò, basta assicurarsi che il mapper sia configurato correttamente. Per esempio:

Opzione 1: Sostenere polimorfico serializzazione/deserializzazione per le classi astratte (e Object classi digitati)

jacksonObjectMapper.enableDefaultTyping(
    ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE); 

Opzione 2: Sostenere polimorfico serializzazione/deserializzazione per le classi astratte (e Object classi digitati), e matrici di questi tipi.

jacksonObjectMapper.enableDefaultTyping(
    ObjectMapper.DefaultTyping.NON_CONCRETE_AND_ARRAYS); 

Riferimento: http://wiki.fasterxml.com/JacksonPolymorphicDeserialization

+1

dove dovrei metterlo (im usando hibernate)? – AndroidTank

+0

In un contesto in cui è definito ObjectMapper e prima che venga utilizzato. Ad esempio, se stai usando un ObjectMapper statico, puoi aggiungere un paragrafo statico e impostarli lì. – AmitW

+1

non sto usando objectmapper. Funziona semplicemente senza alcuna definizione. – AndroidTank

Problemi correlati