Использование Jackson ObjectMapper для сериализации имени подкласса в JSON, а не суперкласса

В следующем коде Джексона /Java, который сериализует объекты в JSON, я получаю следующее:

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

Однако то, что я действительно хочу получить, следующее:

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

Есть ли что-то, что я могу сделать для AnimalContainer, чтобы получить тип выполнения ( "собака", "кошка" ) объекта, а не "животное" )? (Изменить: я знаю, что имя карты происходит от имен метода getter- и setter-method.). Единственный способ, которым я могу это сделать, - это в AnimalContainer иметь атрибут каждого типа Животное, имеет сеттеры и геттеры для всех из них, и обеспечивает, чтобы только один оценивался за раз. Но это побеждает цель иметь Суперкласс животных и просто кажется неправильным. И в моем реальном коде у меня на самом деле есть дюжина подклассов, а не только "собака" и "кошка". Есть ли лучший способ сделать это (возможно, используя аннотации)? Мне также нужно решение для десериализации.

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

Ответ 1

В соответствии с этот анонс, Jackson 1.5 реализует полную обработку полиморфного типа, и теперь соединительная линия имеет встроенный код.

Есть два простых способа сделать эту работу:

  • Добавить аннотацию @JsonTypeInfo в супертипе (Animal here), OR
  • Настроить сопоставление объектов, вызвав ObjectMapper.enableDefaultTyping() (но если это так, Animal должен быть абстрактным)

Ответ 2

Это, вероятно, не тот ответ, который вы ищете, но есть планы по внедрению надлежащей "полиморфной десериализации" (и необходимой поддержки для его сериализации), для версии Jackson 1.4 или так (то есть не следующей версии 1.3, но после этого).

Для текущей версии вам необходимо реализовать пользовательские сериализаторы/десериализаторы: я бы, вероятно, просто определил метод factory для десериализации и тип getter для serializer (определите 'getAnimalType' или что-то в абстрактном базовом классе как абстрактное, переопределить в sub -классы - или даже просто реализовать в базовом классе, имя выходного класса класса экземпляра?).

В любом случае, на всякий случай это имеет значение, вот основные проблемы, связанные с реализацией обработки подклассов с помощью JSON и без языка схемы (поскольку json на самом деле не очень широко используется):

  • как разделять данные (bean значения свойств) из метаданных (информация типа, необходимая только для создания соответствующих подклассов) - должна храниться отдельно, но JSON как формат не имеет способа определить (может использовать соглашение об именах)
  • как добавить собственные аннотации для создания и использования таких метаданных; и не зависит от особенностей языка (не обязательно привязывать к именам классов java, например).

Это разрешимые проблемы, но не тривиально легко решить.: -)

Ответ 3

Это единственный способ, которым я могу это сделать, и он уродлив. Есть ли лучший способ?

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