使用 Jackson ObjectMapper 将子类名称序列化为 JSON,而不是超类
-
19-09-2019 - |
题
在以下将对象序列化为 JSON 的 Jackson/Java 代码中,我得到了以下结果:
{"animal":{"x":"x"}}
然而,我真正想要得到的是:
{"dog":{"x":"x"}}
我可以对 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 {}
}
解决方案
按照 本公告, ,Jackson 1.5 实现了完整的多态类型处理,并且 trunk 现在集成了该代码。
有两种简单的方法可以实现此目的:
- 在超类型中添加 @JsonTypeInfo 注释(此处为 Animal),或者
- 通过调用 ObjectMapper.enableDefaultTyping() 配置对象映射器(但如果是这样,Animal 需要是抽象类型)
其他提示
这可能不是您正在寻找的答案,但计划为 Jackson 版本 1.4 左右(即,实现适当的“多态反序列化”(以及对其序列化的必要支持))。不是下一个,1.3,而是之后的一个)。
对于当前版本,您必须实现自定义序列化器/反序列化器:我可能只是定义用于反序列化的工厂方法,以及用于序列化器的类型获取器(将“getAnimalType”或抽象基类中的任何内容定义为抽象,在子类中重写——或者甚至只是在基类中实现,实例类的输出类名?)。
不管怎样,以防万一,下面是使用 JSON 实现子类处理而不使用模式语言的潜在问题(因为 json 并没有真正广泛使用的语言):
- 如何将数据(bean 属性值)与元数据(构造适当的子类所需的类型信息)分开——必须保持分离,但 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;
}
}