
本文详解如何在 jackson 自定义 `jsonserializer` 中正确序列化多态集合中的子类实例(如 `dog`),避免字段丢失问题,通过 `writeobjectfield` 实现自动委托序列化,并补充反序列化注意事项。
在使用 Jackson(尤其是 XmlMapper)处理多态对象(如 List
根本原因在于:jg.writeNullField("Dog") 仅向输出流写入一个名为 "Dog" 的 null 值字段,完全绕过了 Jackson 的类型发现、注解解析与字段遍历机制;而单独序列化 dog 时,Jackson 默认使用标准 Bean 序列化器,自动读取 @JsonProperty、可见性规则及字段值,因此能正常输出
✅ 正确做法是:在自定义序列化器中,将子对象交还给 Jackson 的默认序列化流程。使用 JsonGenerator.writeObjectField(String fieldName, Object value) 方法——它会自动根据 value 的实际类型(如 Dog),查找并委托其对应的序列化器(可能是默认 BeanSerializer 或其他自定义器),从而完整输出所有可序列化字段。
以下是修复后的 ZooSerializer 完整实现:
public class ZooSerializer extends StdSerializer{ public ZooSerializer() { this(null); } public ZooSerializer(Class t) { super(t); } @Override public void serialize(Zoo zoo, JsonGenerator jg, SerializerProvider sp) throws IOException { jg.writeStartObject(); for (Animal animal : zoo.animals) { String typeName = animal.getClass().getSimpleName(); // ✅ 关键:委托 Jackson 自动序列化 animal 实例 jg.writeObjectField(typeName, animal); } jg.writeEndObject(); } }
配合示例测试代码运行后,输出即变为符合预期的嵌套 XML:
Collie 6
⚠️ 重要注意事项:
-
反序列化需配套自定义 JsonDeserializer:当前序列化输出无类型标识(如 @class 字段或 XML 属性),Jackson 无法自动推断
标签应反序列化为 Dog 实例。若需反序列化,必须实现 ZooDeserializer,解析元素名(如 "Dog")并手动构造对应子类对象。 - 字段可见性需保持一致:确保 Dog.breed/Dog.age 满足 Jackson 默认可见性规则(如 public 字段,或配置 MapperFeature.INFER_PROPERTY_MUTATORS = true)。
- 避免重复注解冲突:若已在 Animal 或子类上使用 @JsonTypeInfo,则无需手动按类名写字段,应优先采用 Jackson 内置多态支持,而非自定义序列化器。
总结:自定义序列化器不是“从零手写 XML”,而是协调与委托。善用 writeObjectField、writeObject 等方法,让 Jackson 复用其成熟的序列化管道,既能保证字段完整性,又能兼容注解、泛型与多态机制。









