objeto - libreria jackson java
Deserialice JSON con Jackson en tipos polimórficos: un ejemplo completo me está dando un error de compilación (4)
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í Tutoriales del programador Bruce (Grandes cosas por cierto)
He trabajado durante 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.
Recibo el siguiente error en el momento de la 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 fragmento 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
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í, 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 estaba 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;
}
}
Entonces 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
, por lo que al deserializar, 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
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
Si usa el rapidxml entonces,
estos cambios pueden ser necesarios
SimpleModule module =
new SimpleModule("PolymorphicAnimalDeserializerModule");
en el método principal
utilizar
new SimpleModule("PolymorphicAnimalDeserializerModule",
new Version(1, 0, 0, null));
en vez de
//Iterator<Entry<String, JsonNode>> elementsIterator = root.getFields();
Iterator<Entry<String, JsonNode>> elementsIterator = root.fields();
//return mapper.readValue(root, animalClass);
return mapper.convertValue(root, animalClass);
y en la función Animal deserialize (), realice los siguientes cambios
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;
}
Esto funciona para más rápido xml.jackson.
Si todavía se queja de los campos de clase.
Use el mismo formato que en el json para los nombres de campo (con "_" -coincidencia).
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; }
Solo necesita una línea antes de la declaración de la clase
Animal
para la serialización / deserialización polimórfica correcta:
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;
Esta línea significa: agregar una metapropiedad en la serialización o leer una
include = JsonTypeInfo.As.PROPERTY
en la deserialización (
include = JsonTypeInfo.As.PROPERTY
) llamada "@class" (
property = "@class"
) que contiene el nombre de clase Java totalmente calificado (
use = JsonTypeInfo.Id.CLASS
).
Entonces, si crea un JSON directamente (sin serialización) recuerde agregar la metapropiedad "@class" con el nombre de clase deseado para la deserialización correcta.
Más información https://github.com/FasterXML/jackson-docs/wiki/JacksonPolymorphicDeserialization
Una manera simple 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ú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 clases tipificadas de objetos)
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 de objeto) y matrices de esos tipos.
jacksonObjectMapper.enableDefaultTyping(
ObjectMapper.DefaultTyping.NON_CONCRETE_AND_ARRAYS);
Referencia: https://github.com/FasterXML/jackson-docs/wiki/JacksonPolymorphicDeserialization