2009-08-24 29 views
10

En el siguiente código de Jackson/Java que serializa objetos en JSON, me estoy haciendo esto:Usando Jackson ObjectMapper para serializar el nombre de subclase en JSON, no es la superclase

{"animal":{"x":"x"}} 

Sin embargo, lo que realmente quiero llegar es la siguiente:

{"dog":{"x":"x"}} 

¿hay algo que pueda hacer para AnimalContainer de modo que consiga el tipo de tiempo de ejecución ("perro", "gato") del objeto, en lugar de "animal")? (Edit: Soy consciente de que el nombre del mapa proviene de los nombres del método getter- y setter-). La única forma en que puedo pensar para hacerlo es dentro de AnimalContainer para tener un atributo de cada tipo de Animal, tener setters y getters para todos ellos, y hacer cumplir que solo uno se valora a la vez. Pero esto frustra el propósito de tener la superclase Animal y parece estar mal. Y en mi código real, en realidad tengo una docena de subclases, no solo "perro" y "gato". ¿Hay una mejor manera de hacer esto (tal vez usando anotaciones de alguna manera)? Necesito una solución para deserializar, también.

public class Test 
{ 
    public static void main(String[] args) throws Exception 
    { 
     AnimalContainer animalContainer = new AnimalContainer(); 
     animalContainer.setAnimal(new Dog()); 

     StringWriter sw = new StringWriter(); // serialize 
     ObjectMapper mapper = new ObjectMapper(); 
     MappingJsonFactory jsonFactory = new MappingJsonFactory(); 
     JsonGenerator jsonGenerator = jsonFactory.createJsonGenerator(sw); 
     mapper.writeValue(jsonGenerator, animalContainer); 
     sw.close(); 
     System.out.println(sw.getBuffer().toString()); 
    } 
    public static class AnimalContainer 
    { 
     private Animal animal; 
     public Animal getAnimal() {return animal;} 
     public void setAnimal(Animal animal) {this.animal = animal;} 
    } 
    public abstract static class Animal 
    { 
     String x = "x"; 
     public String getX() {return x;} 
    } 
    public static class Dog extends Animal {} 
    public static class Cat extends Animal {} 
} 

Respuesta

9

Según this announement, Jackson 1.5 implementa el manejo completo del tipo polimórfico, y el tronco ahora tiene ese código integrado.

Hay dos formas sencillas de realizar este trabajo:

  • Añadir @JsonTypeInfo anotación en supertipo (Animal aquí), o
  • Configurar el objeto del asignador llamando ObjectMapper.enableDefaultTyping() (pero si es así, Animal necesita ser de tipo abstracto)
+1

¿hay alguna manera de resolver con 'ObjectMapper.enableDefaultTyping()' sin la necesidad de que Animal sea abstracto? Estoy usando Jackson 1.9 y lo he intentado pero todavía no funciona ... –

+0

La tipificación predeterminada puede usar diferentes tipos de criterios, consulte javadocs, incluida la definición personalizada de a qué se debe aplicar. – StaxMan

0

Esta es la única forma en que se me ocurre hacerlo, y es feo. ¿Hay una mejor manera?

@JsonWriteNullProperties(false) 
    public static class AnimalContainer 
    { 
     private Animal animal; 

     public Animal getCat() 
     { 
     return animal instanceof Cat ? animal : null; 
     } 
     public void setCat(Cat cat) 
     { 
     this.animal = cat; 
     } 
     public Animal getDog() 
     { 
     return animal instanceof Dog ? animal : null; 
     } 
     public void setDog(Dog dog) 
     { 
     this.animal = dog; 
     } 
     public Animal getFish() 
     { 
     return animal instanceof Fish ? animal : null; 
     } 
     public void setFish(Fish fish) 
     { 
     this.animal = fish; 
     } 
    } 
2

Esto no probablemente no es la respuesta que está buscando, pero hay planes para implementar adecuada "deserialización polimórfica" (y el apoyo necesario en la serialización para ello), para Jackson versión 1.4 o menos (es decir, la próxima uno, 1.3, pero uno después de eso).

Para la versión actual, debe implementar serializadores/deserializadores personalizados: probablemente defina el método de fábrica para la deserialización y escriba getter para serializador (defina 'getAnimalType' o lo que sea en la clase base abstracta como abstract, anule en sub- clases - o incluso simplemente implementar en la clase base, nombre de la clase de salida de la clase de instancia?).

De todos modos, en caso de que importa, aquí están los problemas subyacentes WRT implementación de manejo de sub-clases con JSON, y sin lenguaje de esquema (ya JSON no realmente han utilizado ampliamente uno):

  • cómo para separar los datos (valores de propiedad del frijol) de los metadatos (solo se necesita información para construir las subclases apropiadas) - se debe mantener separado, pero JSON como formato no tiene forma de definirlo (podría usar la convención de nomenclatura)
  • cómo agregar las anotaciones adecuadas para generar y usar tales metadatos; y sin depender de las características específicas del idioma (no debería tener que vincularse a los nombres de clase de Java, por ejemplo)

Estos son problemas solucionables, pero no trivialmente fáciles de resolver. :-)

+0

Al menos esta es una confirmación de que no hay forma de hacer esto dentro de Jackson (todavía), que no me falta algo; tiene que hacerse de la manera difícil. – seansand

+0

Correcto: no se me ocurre una manera de hacerlo de forma sencilla (sin serializador/deserializador personalizado) con Jackson 1.2 o versiones anteriores. – StaxMan

+0

FWIW, Jackson versión 1.5 tendrá soporte para el manejo completo del polimorfismo para la deserialización (acaba de lanzarse 1.4). – StaxMan

Cuestiones relacionadas