如何用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 (tokenJsonToken.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注解,明确指定自定义反序列化器,绕开泛型擦除的影响。
- 编写适配两种格式的反序列化器:
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) } } }
- 修改Test类的字段注解:
class Test { @JsonDeserialize(using = IntegerListDeserializer.class) List<Integer> a; }
- 初始化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

