Deserializar JSON con Jackson en Tipos Polimórficos - Un Ejemplo completo me está dando un error de compilación


Estoy tratando de trabajar a través de un tutorial del programador Bruce que se supone que permite la deserialización de JSON polimórfico.

La lista completa se puede encontrar aquí Programador Bruce tutoriales (Gran material por cierto)

He trabajado a través de los primeros cinco sin problemas, pero he encontrado un inconveniente en el último (Ejemplo 6), que por supuesto es el que realmente necesito para trabajar.

Estoy recibiendo el siguiente error en tiempo de compilación

El método readValue (JsonParser, Class) en el tipo ObjectMapper no es aplicable para los argumentos (ObjectNode, Class)

Y está siendo causado por el trozo de código

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

Específicamente por la línea

Devuelve el mapeador.readValue(raíz, animalClass);

¿Alguien se ha encontrado con esto antes y si es así, había una solución?

Agradecería cualquier ayuda que alguien pueda dar Gracias de antemano Jon D.

Author: Timo, 2015-05-21

3 answers

Como prometí, estoy poniendo un ejemplo de cómo usar anotaciones para serializar/deserializar objetos polimórficos, basé este ejemplo en la clase Animal del tutorial que estabas leyendo.

En primer lugar su clase Animal con las anotaciones Json para las subclases.

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

}

Luego tus subclases, Dog y 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;

}

Como puedes ver, no hay nada especial para Cat y Dog, el único que sabe de ellos es la clase abstract Animal, así que cuando deserializar, podrás apuntar a Animal y el ObjectMapper devolverá la instancia real como se puede ver en la siguiente prueba:

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

    }
}

Salida después de ejecutar la clase Test:

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

Deserialized dogJson Class: Dog

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

Deserialized catJson Class: Cat

Espero que esto ayude,

José Luis

 79
Author: jbarrueta,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-22 00:47:40

Una forma sencilla de habilitar la serialización / deserialización polimórfica a través de la biblioteca Jackson es configurar globalmente el mapeador de objetos Jackson (jackson.databind.ObjectMapper) para agregar información, como el tipo de clase concreto, para ciertos tipos de clases, como las clases abstractas.

Para hacer eso, solo asegúrese de que su mapeador esté configurado correctamente. Por ejemplo:

Opción 1: Admite la serialización / deserialización polimórfica para clases abstractas (y Objetos tipeados clases)

jacksonObjectMapper.enableDefaultTyping(
    ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE); 

Opción 2: Admite la serialización / deserialización polimórfica para clases abstractas (y clases de tipo objeto) y matrices de esos tipos.

jacksonObjectMapper.enableDefaultTyping(
    ObjectMapper.DefaultTyping.NON_CONCRETE_AND_ARRAYS); 

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

 1
Author: AmitW,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-02-01 14:54:21

Si usa fasterxml,

Estos cambios podrían ser necesarios

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;

En el método principal {

Use

SimpleModule module =
  new SimpleModule("PolymorphicAnimalDeserializerModule");

En lugar de

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

Y en la función Animal deserialize (), realice los siguientes cambios

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

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

Esto funciona para fasterxml.jackson. Si todavía se queja de los campos de clase. Utilice el mismo formato que en el json para los nombres de campo (con "_" -subrayado). como este
//mapper.setPropertyNamingStrategy(new CamelCaseNamingStrategy()); podría no ser compatible.

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
Author: ravi.zombie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-08-19 06:22:18