You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让Jackson的@JsonAnySetter捕获所有属性(含已映射字段)

解决方案

方案一:手动初始化已映射字段(适合字段少的场景)

直接在实体类的初始化块里,把已声明的字段手动加入dynamicFields,配合@JsonAnySetter捕获未映射字段,就能让dynamicFields包含所有属性:

data class Student(val name: String) {
    val dynamicFields: MutableMap<String, Any?> = mutableMapOf()

    // 初始化时把已映射的name字段加入map
    init {
        dynamicFields["name"] = name
    }

    @JsonAnySetter
    fun setDynamicField(key: String, value: Any?) {
        dynamicFields[key] = value
    }
}

测试用给定的JSON:

{
  "name": "John",
  "address": "Wick"
}

反序列化后,student.dynamicFields会同时包含name: "John"和address: "Wick",实体类的name字段也能正常访问。

如果有嵌套对象,只要嵌套的实体类也用同样的方式处理即可:

data class Address(val street: String) {
    val dynamicFields: MutableMap<String, Any?> = mutableMapOf()

    init {
        dynamicFields["street"] = street
    }

    @JsonAnySetter
    fun setDynamicField(key: String, value: Any?) {
        dynamicFields[key] = value
    }
}

data class Student(val name: String, val address: Address) {
    val dynamicFields: MutableMap<String, Any?> = mutableMapOf()

    init {
        dynamicFields["name"] = name
        dynamicFields["address"] = address
    }

    @JsonAnySetter
    fun setDynamicField(key: String, value: Any?) {
        dynamicFields[key] = value
    }
}

方案二:自定义Jackson Module(适合多字段、嵌套场景,一劳永逸)

如果实体类字段多或者嵌套层级复杂,手动加字段太繁琐,可以自定义一个Jackson Module,让Spring自动注入,不用直接操作ObjectMapper,就能自动把所有已映射字段加入指定的Map。

步骤1:定义标记注解

先写一个注解,标记哪个Map字段用来存放所有属性:

@Target(AnnotationTarget.FIELD)
@Retention(AnnotationRetention.RUNTIME)
annotation class AllFieldsMap

步骤2:实现自定义Module

这个Module会修改Jackson的反序列化逻辑,自动把所有已映射字段注入到标记了@AllFieldsMap的Map里:

class AllFieldsCollectorModule : SimpleModule() {
    override fun setupModule(context: SetupContext) {
        super.setupModule(context)
        context.addBeanDeserializerModifier(object : BeanDeserializerModifier() {
            override fun modifyDeserializer(
                config: DeserializationConfig,
                beanDesc: BeanDescription,
                deserializer: JsonDeserializer<*>
            ): JsonDeserializer<*> {
                // 只处理带有@AllFieldsMap字段的实体类
                val targetMapField = beanDesc.beanClass.declaredFields.find {
                    it.isAnnotationPresent(AllFieldsMap::class.java)
                } ?: return deserializer

                if (deserializer is BeanDeserializer) {
                    return object : BeanDeserializer(deserializer) {
                        override fun deserialize(p: JsonParser, ctxt: DeserializationContext): Any {
                            // 先完成默认反序列化,得到实体对象
                            val bean = super.deserialize(p, ctxt)
                            // 获取当前类所有已映射的属性名
                            val mappedFields = beanDesc.findProperties().map { it.name }

                            mappedFields.forEach { fieldName ->
                                // 反射获取字段值
                                val field = beanDesc.beanClass.getDeclaredField(fieldName)
                                field.isAccessible = true
                                val fieldValue = field.get(bean)

                                // 将字段名和值存入目标Map
                                targetMapField.isAccessible = true
                                @Suppress("UNCHECKED_CAST")
                                val allFieldsMap = targetMapField.get(bean) as MutableMap<String, Any?>
                                allFieldsMap[fieldName] = fieldValue
                            }
                            return bean
                        }
                    }
                }
                return deserializer
            }
        })
    }
}

步骤3:在Spring中注册Module

写一个配置类,把自定义Module注册到Spring的Bean容器,Jackson会自动加载这个Module:

@Configuration
class JacksonConfig {
    @Bean
    fun allFieldsCollectorModule(): Module {
        return AllFieldsCollectorModule()
    }
}

步骤4:改造实体类

给存放所有字段的Map加上@AllFieldsMap注解,保留@JsonAnySetter处理未映射字段:

data class Student(val name: String) {
    @AllFieldsMap
    val dynamicFields: MutableMap<String, Any?> = mutableMapOf()

    @JsonAnySetter
    fun setDynamicField(key: String, value: Any?) {
        dynamicFields[key] = value
    }
}

// 嵌套对象同样处理
data class Address(val street: String, val zipCode: String) {
    @AllFieldsMap
    val dynamicFields: MutableMap<String, Any?> = mutableMapOf()

    @JsonAnySetter
    fun setDynamicField(key: String, value: Any?) {
        dynamicFields[key] = value
    }
}

这样不管实体类有多少字段,或者嵌套多少层,反序列化后dynamicFields都会自动包含所有已映射和未映射的属性,不用手动维护字段列表。


内容的提问来源于stack exchange,提问作者wjtk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 16:23:12