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

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

Вопрос

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

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

Однако то, что я на самом деле хочу получить, это:

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

Есть ли что-нибудь, что я могу сделать с animalContainer, чтобы получить тип среды выполнения ("dog", "cat") объекта вместо "animal")? (Редактировать: Я знаю, что имя карты происходит от имен методов получения и установки.) Единственный способ, который я могу придумать, чтобы сделать это, - это внутри animalContainer иметь атрибут каждого типа Animal, иметь установщики и добытчики для всех из них и обеспечить, чтобы одновременно оценивался только один.Но это противоречит цели создания суперкласса Animal и просто кажется неправильным.И в моем реальном коде у меня на самом деле есть дюжина подклассов, а не только "dog" и "cat".Есть ли лучший способ сделать это (возможно, каким-то образом используя аннотации)?Мне также нужно решение для десериализации.

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 {} 
}
Это было полезно?

Решение

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

Есть два простых способа заставить это сработать:

  • Добавьте аннотацию @JsonTypeInfo в supertype (здесь Animal), ИЛИ
  • Настройте objectmapper, вызвав ObjectMapper.enableDefaultTyping() (но если это так, Animal должен быть абстрактного типа)

Другие советы

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

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

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

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

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

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

   @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;
      }
   }
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top