Jackson如何将两个JSON字段反序列化为单个合并属性?
问题
我的MongoDB文档包含引用其他文档的结构,示例如下:
{ "_id": ..., "clientId": "foo123", "clientKind": "User", "otherProps": ... }
我需要将这类带有client...前缀的引用字段,转换为Java/Kotlin中的引用对象,比如:
new UserReference(id = "foo123")
或者泛型形式:
new Reference<User>(id = "foo123")
规则是:...Kind字段对应引用的类型(比如clientKind的值User对应User类型),...Id字段对应引用对象的id属性。希望通过Jackson自动完成这个转换,不需要自定义getter或处理私有属性,该怎么实现?
解决方案
方案1:自定义Jackson反序列化器(推荐)
通过自定义JsonDeserializer实现两个字段合并为一个引用对象的逻辑,步骤如下:
- 定义通用引用类(以Kotlin为例)
open class Reference<T>(val id: String) class UserReference(id: String) : Reference<User>(id)
- 编写自定义反序列化器
针对XxxId+XxxKind的字段对编写反序列化逻辑,匹配前缀并生成对应引用实例:
class ReferenceDeserializer : JsonDeserializer<Reference<*>>() { override fun deserialize(p: JsonParser, ctxt: DeserializationContext): Reference<*> { val node = p.codec.readTree<JsonNode>(p) // 提取目标前缀(此处以"client"为例,可扩展为自动识别所有前缀) val prefix = "client" val id = node.get("${prefix}Id").asText() val kind = node.get("${prefix}Kind").asText() return when(kind) { "User" -> UserReference(id) // 可添加更多类型分支 else -> throw IllegalArgumentException("不支持的引用类型: $kind") } } }
- 在实体类中应用注解
在需要转换的属性上添加反序列化器注解:
data class YourEntity( val _id: String, @JsonDeserialize(using = ReferenceDeserializer::class) val client: Reference<*>, val otherProps: Any )
方案2:使用@JsonCreator结合构造器参数
无需额外编写反序列化器,直接通过实体类构造器接收两个字段并合并为引用对象:
data class YourEntity( val _id: String, val client: Reference<*>, val otherProps: Any ) { @JsonCreator constructor( @JsonProperty("_id") _id: String, @JsonProperty("clientId") clientId: String, @JsonProperty("clientKind") clientKind: String, @JsonProperty("otherProps") otherProps: Any ) : this( _id, when(clientKind) { "User" -> UserReference(clientId) else -> throw IllegalArgumentException("不支持的引用类型") }, otherProps ) }
这种方式完全依托Jackson的构造器注入能力,不需要自定义getter或私有属性处理。
方案3:全局扩展BeanDeserializerModifier
如果需要批量处理多个实体类的这类字段转换,可以通过BeanDeserializerModifier扩展Jackson默认反序列化逻辑,自动识别XxxId+XxxKind字段并合并为引用属性。核心逻辑与自定义反序列化器一致,但能实现全局生效,无需逐个实体添加注解。
内容的提问来源于stack exchange,提问作者Ondra Žižka
相关产品推荐
相关产品推荐

