Usando Jackson ObjectMapper para serializar o nome da subclasse em JSON, e não a superclasse

StackOverflow https://stackoverflow.com/questions/1325074

Pergunta

No código a seguir Jackson / Java que serializa objetos em JSON, eu estou recebendo este:

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

No entanto, o que eu realmente quero começar é esta:

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

Existe algo que eu possa fazer para AnimalContainer para que eu obter o tipo de tempo de execução ( "cão", "gato") do objeto, em vez de "animal")? ( Editar: Estou ciente de que o nome do mapa vem dos nomes de métodos getter- e setter-.) A única maneira que eu posso pensar em fazer é dentro AnimalContainer para tem um atributo de cada tipo de animal, tem setters e getters para todos eles, e reforça que apenas é avaliado em um tempo. Mas esta derrota o propósito de ter o animal superclasse e apenas parece errado. E no meu código real eu realmente ter uma dúzia de subclasses, e não apenas "cão" e "gato". Existe uma maneira melhor de fazer isso (talvez usando anotações de alguma forma)? Eu preciso de uma solução para desserialização, também.

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 {} 
}
Foi útil?

Solução

De acordo com este announement , Jackson 1,5 implementos completo manuseio tipo polimórfico, e tronco agora que o código integrado.

Existem duas maneiras simples de fazer este trabalho:

  • Adicionar @JsonTypeInfo anotação no supertipo (aqui Animal), OU
  • Configurar o mapeador objeto chamando ObjectMapper.enableDefaultTyping () (mas se assim for, Animal precisa ser tipo abstrato)

Outras dicas

Isto provavelmente não é a resposta que você está procurando, mas há planos para implementar "desserialização polimórfica" adequada (e apoio necessário na serialização para ele), para Jackson versão 1.4 ou mais (ou seja, não o próximo, 1.3, mas depois disso).

Para versão atual, você tem que implementar serializers personalizados / deserializadores: Eu provavelmente apenas definir método de fábrica para desserialização e tipo getter para serializer (definir 'getAnimalType' ou o que quer na classe base abstrata como abstrato, override em subclasses -?. ou mesmo apenas implementar na classe base, nome da classe de saída de classe exemplo)

De qualquer forma, apenas no caso que interessa, os problemas aqui estão subjacentes WRT implementar manipulação de sub-classes com JSON, e sem linguagem de esquema (uma vez que json realmente não têm amplamente utilizado):

  • como separar dados (valores de propriedade de feijão) de metadados (tipo de informação só precisava para construir subclasses adequadas) - devem ser mantidos separados, mas JSON como formato tem nenhuma maneira de definir (poderia usar convenção de nomenclatura)
  • como adicionar anotações apropriadas para gerar e utilizar tais metadados; e sem dependendo dos recursos da linguagem específicos (não deveria ter que amarrar a nomes de classe java, por exemplo)

Estes são problemas que podem ser resolvidos, mas não tão fácil de resolver. : -)

Esta é a única maneira que eu posso pensar em fazê-lo, e é feio. Existe uma maneira melhor?

   @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;
      }
   }
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top