Jackson嵌套场景下type类型名称字段缺失问题及解决
问题描述
现有以下Kotlin接口及实现类:
////////////////// Subject ////////////////// interface Subject interface PluginSubject : Subject { val id: String } @JsonTypeInfo( use = JsonTypeInfo.Id.NAME, property = TYPE_FIELD_NAME // "type" ) abstract class AbstractSubject: Subject @JsonTypeName(SUBJECT_TYPE_PLUGIN) // "plugin" data class PluginSubjectImpl( override val id: String ): AbstractSubject(), PluginSubject ////////////////// Packet ////////////////// interface RequestPacket : Packet { // ... val subject: Subject } @JsonTypeInfo( use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = TYPE_FIELD_NAME // "type" ) abstract class AbstractPacket( // ... ) : Packet @JsonTypeName(PACKET_TYPE_REQUEST) // "request" data class RequestPacketImpl( // ... override val subject: Subject ) : AbstractPacket(id, PACKET_TYPE_REQUEST, time), RequestPacket
单独序列化PluginSubjectImpl实例时结果正常,包含type字段:
{ "type" : "plugin", "id" : "subject-id" }
但将PluginSubjectImpl作为RequestPacketImpl的subject字段序列化时,subject的type字段缺失:
{ ... "subject" : { "id" : "subject-id" } ... }
请问为何subject作为字段时type字段不输出?该如何修复?
原因分析
Jackson序列化时,是根据字段声明的静态类型而非对象的实际运行时类型来决定是否输出多态类型信息:
- 单独序列化
PluginSubjectImpl时,Jackson处理的是具体类,它继承自带有@JsonTypeInfo注解的AbstractSubject,因此会按照注解要求输出type字段。 - 当
subject作为RequestPacketImpl的字段时,字段声明的类型是Subject接口——这个接口本身没有标注@JsonTypeInfo,Jackson不知道需要为该类型输出多态类型信息,即使实际对象是PluginSubjectImpl,也不会自动添加type字段。
修复方案
方案一:给Subject接口全局添加多态类型注解
直接在顶层Subject接口上标注@JsonTypeInfo,让所有声明为Subject类型的字段都自动输出type:
@JsonTypeInfo( use = JsonTypeInfo.Id.NAME, property = TYPE_FIELD_NAME // "type" ) interface Subject interface PluginSubject : Subject { val id: String } @JsonTypeName(SUBJECT_TYPE_PLUGIN) // "plugin" data class PluginSubjectImpl( override val id: String ): AbstractSubject(), PluginSubject // AbstractSubject可保留或移除@JsonTypeInfo,接口已声明全局规则 abstract class AbstractSubject: Subject
方案二:为subject字段单独配置类型信息
如果不想修改全局接口配置,可在RequestPacketImpl的subject字段上单独添加@JsonTypeInfo注解,仅对该字段生效:
@JsonTypeName(PACKET_TYPE_REQUEST) // "request" data class RequestPacketImpl( // ... @JsonTypeInfo( use = JsonTypeInfo.Id.NAME, property = TYPE_FIELD_NAME // "type" ) override val subject: Subject ) : AbstractPacket(id, PACKET_TYPE_REQUEST, time), RequestPacket
方案三:指定字段的序列化类型为AbstractSubject
利用AbstractSubject已有的@JsonTypeInfo配置,通过@JsonSerialize指定字段的序列化类型,让Jackson按该类型规则输出type:
@JsonTypeName(PACKET_TYPE_REQUEST) // "request" data class RequestPacketImpl( // ... @JsonSerialize(as = AbstractSubject::class) override val subject: Subject ) : AbstractPacket(id, PACKET_TYPE_REQUEST, time), RequestPacket
内容的提问来源于stack exchange,提问作者Chuanwise
相关产品推荐
相关产品推荐

