
本文详解如何在 jackson 自定义 `stdserializer` 中正确序列化多态集合中的子类字段(如 `dog` 的 `breed` 和 `age`),避免出现 `
在使用 Jackson(尤其是 XmlMapper)处理含继承关系的对象(如 Animal 及其子类 Dog)时,若为容器类(如 Zoo)编写自定义序列化器,常会遇到“子类实例能独立序列化,但嵌入容器后字段丢失”的问题。根本原因在于:原始代码中调用 jg.writeNullField("Dog") 仅写入一个空字段名,并未触发对 Dog 对象本身的序列化流程;而 Jackson 对单个 Dog 实例的序列化(如 writeValueAsString(dog))则自动应用了默认 Bean 序列化器,因此能输出完整 XML。
✅ 正确做法是:在自定义 ZooSerializer.serialize() 中,对每个 Animal 子实例,调用 jg.writeObjectField(fieldName, value)。该方法会:
- 自动识别 value 类型(如 Dog);
- 委托 Jackson 内置的、已注册的对应类型序列化器(如 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(); jg.writeObjectField(typeName, animal); // ✅ 关键:委托 Jackson 序列化子对象 } jg.writeEndObject(); } }
运行测试后,输出即变为符合预期的嵌套 XML:
Collie 6
⚠️ 重要注意事项:
-
反序列化需配套实现:由于此方案绕过了 @JsonTypeInfo 等标准多态机制,仅靠 writeObjectField 无法自动还原类型信息。若需从 XML 反序列化回 Zoo,必须同时提供自定义 ZooDeserializer,并在其中根据 XML 标签名(如
)手动构造对应子类实例并添加到 zoo.animals。 - 字段可见性:确保子类字段(如 Dog.breed)具有默认可访问性(public 或有 public getter)。若使用 private 字段,需添加 @JsonProperty 或配置 Visibility。
-
XML 特性兼容:XmlMapper 会将 writeObjectField("Dog", dog) 渲染为
... 标签,与 JSON 的 { "Dog": { ... } } 语义一致,无需额外适配。
通过这一模式,你既能保持对容器结构的精细控制(如动态字段名),又能无缝复用 Jackson 对子对象的标准序列化能力,是处理复杂 XML 多态输出的可靠实践。









