Java 使用 Jackson ObjectMapper 将子类名称序列化为 JSON,而不是超类
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1325074/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Using Hymanson ObjectMapper to serialize the subclass name into JSON, not the superclass
提问by seansand
In the following Hymanson/Java code that serializes objects into JSON, I am getting this:
在以下将对象序列化为 JSON 的 Hymanson/Java 代码中,我得到了这个:
{"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.
我可以对 AnimalContainer 做些什么,以便获得对象的运行时类型(“狗”、“猫”),而不是“动物”)? (编辑:我知道地图名称来自 getter 和 setter 方法名称。)我能想到的唯一方法是在 AnimalContainer 中拥有每种类型的 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 {}
}
采纳答案by StaxMan
As per this announement, Hymanson 1.5 implements full polymorphic type handling, and trunk now has that code integrated.
根据此公告,Hymanson 1.5 实现了完整的多态类型处理,并且主干现在集成了该代码。
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)
- 在超类型中添加@JsonTypeInfo 注释(此处为Animal),或
- 通过调用 ObjectMapper.enableDefaultTyping() 配置对象映射器(但如果是这样,Animal 需要是抽象类型)
回答by seansand
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;
}
}
回答by StaxMan
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 Hymanson version 1.4 or so (i.e. not the next one, 1.3, but one after that).
这可能不是您正在寻找的答案,但是对于 Hymanson 1.4 版左右(即不是下一个,1.3,而是之后的一个),有计划实施适当的“多态反序列化”(以及对其序列化的必要支持)那)。
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?).
对于当前版本,您必须实现自定义序列化器/反序列化器:我可能只定义用于反序列化的工厂方法,并为序列化器键入 getter(将 'getAnimalType' 或抽象基类中的任何内容定义为抽象,在子类中覆盖 - 或甚至只是在基类中实现,输出实例类的类名?)。
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):
无论如何,为了以防万一,这里是使用 JSON 实现子类处理的潜在问题,而没有模式语言(因为 json 并没有真正广泛使用):
- 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)
- 如何将数据(bean 属性值)与元数据(类型信息只需要构建适当的子类)分开——必须保持分开,但 JSON 作为格式无法定义(可以使用命名约定)
- 如何添加适当的注释以生成和使用此类元数据;并且不依赖于语言的特定功能(例如,不必绑定到 Java 类名)
These are solvable problems, but not trivially easy to solve. :-)
这些都是可以解决的问题,但并不容易解决。:-)