Deserializar JSON con Jackson en tipos polimórficos: un ejemplo completo me da un error de comstackción

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

La lista completa se puede encontrar aquí Tutoriales de Bruce del progtwigdor (Cosas grandiosas por cierto)

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

Recibo el siguiente error en tiempo de comstackció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 animalClass = null; Iterator<Entry> elementsIterator = root.getFields(); while (elementsIterator.hasNext()) { Entry 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

return mapper.readValue (root, animalClass);

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

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

Como prometí, voy a poner 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.

Antes que nada, tu clase de Animal con las Anotaciones de 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 puede ver, no hay nada especial para Cat and Dog , el único que sabe de ellos es la clase abstract Animal , así que cuando deserialice, apuntará a Animal y ObjectMapper devolverá la instancia real como 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 de 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

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 concreta, para ciertos tipos de clases, como clases abstractas.

Para hacer eso, solo asegúrate de que tu asignador esté configurado correctamente. Por ejemplo:

Opción 1: soporte serialización polimórfica / deserialización para clases abstractas (y clases de tipo Object)

 jacksonObjectMapper.enableDefaultTyping( ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE); 

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

 jacksonObjectMapper.enableDefaultTyping( ObjectMapper.DefaultTyping.NON_CONCRETE_AND_ARRAYS); 

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

Si usa el fasterxml, entonces,

estos cambios pueden 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 método principal–

utilizar

 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 cambios a continuación

 //Iterator> elementsIterator = root.getFields(); Iterator> 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. Use el mismo formato que en json para los nombres de campo (con “_” -underscore). como esto
//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; }