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

如何用Jackson自定义反序列化兼容List的两种JSON格式?

问题描述

我定义了如下Java类:

class Test {
    List<Integer> a;
}

希望能正确解析以下两种格式的JSON:

{ "a" : "[1, 2, 3]" }
{ "a" : [1, 2, 3] }

使用Jackson进行反序列化时,抛出如下异常:

Exception in thread "main" java.lang.IllegalArgumentException: Cannot deserialize value of type java.util.ArrayList<java.lang.Integer> from String value (token JsonToken.VALUE_STRING)

我尝试编写了如下自定义反序列化器,但未生效(未被调用),推测是泛型擦除导致的:

val jackson = ObjectMapper().apply {
    registerModule(SimpleModule().addDeserializer(ArrayList::class.java, object : JsonDeserializer<ArrayList<Integer>>() {
        override fun deserialize(parser: JsonParser, context: DeserializationContext) =
            parser.text.trim('[', ']').split(',').map { it.toInt() } as ArrayList<Integer>
    }))
}

请问如何编写有效的自定义反序列化器来解决该问题?


解决方案

你的推测没错,问题根源是泛型擦除:你注册的ArrayList反序列化器,无法匹配List<Integer>字段的泛型类型绑定逻辑,Jackson会优先使用针对List的默认反序列化逻辑,导致自定义逻辑不生效。下面提供三种可行方案:

方案一:字段级指定反序列化器(最简直接)

直接在实体类的目标字段上添加@JsonDeserialize注解,明确指定自定义反序列化器,绕开泛型擦除的影响。

  1. 编写适配两种格式的反序列化器:
class IntegerListDeserializer : JsonDeserializer<List<Int>>() {
    override fun deserialize(parser: JsonParser, context: DeserializationContext): List<Int> {
        return when (parser.currentToken) {
            // 处理JSON数组格式,复用默认反序列化逻辑
            JsonToken.START_ARRAY -> context.readValue(parser, object : TypeReference<List<Int>>() {})
            // 处理字符串格式,解析为整数列表
            JsonToken.VALUE_STRING -> {
                val text = parser.text.trim('[', ']')
                if (text.isBlank()) emptyList() else text.split(",").map { it.trim().toInt() }
            }
            else -> throw context.mappingException(List::class.java)
        }
    }
}
  1. 修改Test类的字段注解:
class Test {
    @JsonDeserialize(using = IntegerListDeserializer.class)
    List<Integer> a;
}
  1. 初始化ObjectMapper(记得注册Kotlin模块):
val jackson = ObjectMapper().apply {
    registerModule(KotlinModule())
}

方案二:全局注册泛型List反序列化器

如果不想修改实体类,可以通过Jackson的TypeFactory创建泛型类型的JavaType,注册对应的反序列化器:

val jackson = ObjectMapper().apply {
    // 构造List<Integer>的泛型类型
    val integerListType = typeFactory.constructParametricType(List::class.java, Int::class.java)
    registerModule(SimpleModule().apply {
        addDeserializer(integerListType, object : JsonDeserializer<List<Int>>() {
            override fun deserialize(parser: JsonParser, context: DeserializationContext): List<Int> {
                return when (parser.currentToken) {
                    JsonToken.START_ARRAY -> context.readValue(parser, object : TypeReference<List<Int>>() {})
                    JsonToken.VALUE_STRING -> {
                        val text = parser.text.trim('[', ']')
                        if (text.isBlank()) emptyList() else text.split(",").map { it.trim().toInt() }
                    }
                    else -> throw context.mappingException(List::class.java)
                }
            }
        })
    })
    registerModule(KotlinModule())
}

该方案会让Jackson处理所有List<Integer>类型时都使用自定义逻辑,适合全局统一处理的场景。

方案三:通用泛型List反序列化器(适配任意元素类型)

如果需要支持更多元素类型的List,可以编写通用反序列化器,通过Jackson上下文获取实际的泛型元素类型:

class GenericListDeserializer : JsonDeserializer<List<*>>() {
    override fun deserialize(parser: JsonParser, context: DeserializationContext): List<*> {
        val type = context.contextualType
        // 获取List的泛型元素类型
        val elementType = (type as ParameterizedType).actualTypeArguments[0] as Class<*>
        
        return when (parser.currentToken) {
            JsonToken.START_ARRAY -> context.readValue(parser, type)
            JsonToken.VALUE_STRING -> {
                val text = parser.text.trim('[', ']')
                if (text.isBlank()) emptyList() else {
                    text.split(",").map { 
                        val trimmed = it.trim()
                        // 根据元素类型做转换,可扩展更多类型
                        when (elementType) {
                            Int::class.java -> trimmed.toInt()
                            String::class.java -> trimmed
                            Long::class.java -> trimmed.toLong()
                            else -> context.readValue(parser.codec.readTree(parser).textValue(), elementType)
                        }
                    }
                }
            }
            else -> throw context.mappingException(List::class.java)
        }
    }

    override fun createContextual(ctxt: DeserializationContext, property: BeanProperty?): JsonDeserializer<*> {
        // 保留泛型类型上下文信息
        return this
    }
}

注册时直接绑定List::class.java:

val jackson = ObjectMapper().apply {
    registerModule(SimpleModule().addDeserializer(List::class.java, GenericListDeserializer()))
    registerModule(KotlinModule())
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 13:23:34