使用 Jackson ObjectMapper 将子类名称序列化为 JSON,而不是超类

发布于 2024-08-03 15:18:58 字数 1489 浏览 5 评论 0原文

在以下将对象序列化为 JSON 的 Jackson/Java 代码中,我得到的是:

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

然而,我真正想要得到的是:

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

我可以对 AnimalContainer 做些什么,以便获得运行时类型 ("dog", "对象的“猫”),而不是“动物”)? 编辑:我知道地图名称来自 getter- 和 setter- 方法名称。) 我能想到的唯一方法是在 AnimalContainer 中每种 Animal 都有一个属性,所有这些 Animal 都有 setter 和 getter,并强制一次只能评估一个。但这违背了拥有 Animal 超类的目的,而且似乎是错误的。在我的真实代码中,我实际上有十几个子类,而不仅仅是“狗”和“猫”。有没有更好的方法来做到这一点(也许以某种方式使用注释)?我还需要一个反序列化的解决方案。

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

In the following Jackson/Java code that serializes objects into JSON, I am getting this:

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

However, what I actually want to get is this:

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

Is there something I can do to AnimalContainer so that I get the runtime type ("dog", "cat") of the object, instead of "animal")? (Edit: I am aware that the map name comes from the getter- and setter- method names.) The only way I can think of to do it is within AnimalContainer to have an attribute of each type of Animal, have setters and getters for all of them, and enforce that only one is valued at a time. But this defeats the purpose of having the Animal superclass and just seems wrong. And in my real code I actually have a dozen subclasses, not just "dog" and "cat". Is there a better way to do this (perhaps using annotations somehow)? I need a solution for deserializing, as well.

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

爱人如己 2024-08-10 15:18:58

根据本公告, Jackson 1.5 实现了完整的多态类型处理,并且 trunk 现在集成了该代码。

有两种简单的方法可以实现此目的:

  • 在超类型中添加 @JsonTypeInfo 注释(此处为 Animal),或者
  • 通过调用 ObjectMapper.enableDefaultTyping() 配置对象映射器(但如果是这样,Animal 需要是抽象类型)

As per this announement, Jackson 1.5 implements full polymorphic type handling, and trunk now has that code integrated.

There are two simple ways to make this work:

  • Add @JsonTypeInfo annotation in supertype (Animal here), OR
  • Configure object mapper by calling ObjectMapper.enableDefaultTyping() (but if so, Animal needs to be abstract type)
会傲 2024-08-10 15:18:58

这可能不是您正在寻找的答案,但计划为 Jackson 版本 1.4 左右(即不是下一个版本 1.3,而是之后的版本)实现适当的“多态反序列化”(以及对其序列化的必要支持)那)。

对于当前版本,您必须实现自定义序列化器/反序列化器:我可能只定义用于反序列化的工厂方法,并为序列化器定义类型 getter(将“getAnimalType”或抽象基类中的任何内容定义为抽象,在子类中重写 - 或者即使只是在基类中实现,输出实例类的类名?)。

无论如何,以防万一,下面是使用 JSON 实现子类处理的潜在问题,并且没有模式语言(因为 json 并没有真正广泛使用):

  • 如何将数据(bean 属性值)与元数据分离(类型信息仅需要构造适当的子类)——必须保持独立,但 JSON 作为格式无法定义(可以使用命名约定)
  • 如何添加适当的注释来生成和使用此类元数据;并且不依赖于语言的特定功能(例如,不必与 java 类名相关)

这些是可以解决的问题,但解决起来并不容易。 :-)

This is probably not the answer you are looking for, but there are plans to implement proper "polymorphic deserialization" (and necessary support on serialization for it), for Jackson version 1.4 or so (i.e. not the next one, 1.3, but one after that).

For current version, you have to implement custom serializers/deserializers: I would probably just define factory method for deserialization, and type getter for serializer (define 'getAnimalType' or whatever in abstract base class as abstract, override in sub-classes -- or even just implement in base class, output class name of instance class?).

Anyway, just in case it matters, here are underlying problems wrt implementing handling of sub-classes with JSON, and without schema language (since json doesn't really have widely used one):

  • how to separate data (bean property values) from metadata (type information only needed to construct proper subclasses) -- must be kept separate, but JSON as format has no way to define (could use naming convention)
  • how to add proper annotations to generate and use such metadata; and without depending on language specific features (shouldn't have to tie to java class names for example)

These are solvable problems, but not trivially easy to solve. :-)

他不在意 2024-08-10 15:18:58

这是我能想到的唯一方法,而且很丑陋。有更好的办法吗?

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

This is the only way I can think of to do it, and it is ugly. Is there a better way?

   @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;
      }
   }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文