如何在Kotlin中反序列化可为字符串或对象的JSON?
处理JSON字段类型不确定的Jackson反序列化方案
针对你遇到的foo字段既可能是字符串也可能是对象的场景,可以通过自定义Jackson反序列化器来统一处理,以下是具体实现步骤:
1. 定义目标数据类
先明确最终要映射的数据结构,把foo统一映射为包含bar属性的对象:
data class MyJson(val hello: Hello) data class Hello(val foo: Foo) data class Foo(val bar: String)
2. 编写自定义反序列化器
创建FooDeserializer类,判断JSON节点类型,分别处理字符串和对象两种情况:
import com.fasterxml.jackson.core.JsonParser import com.fasterxml.jackson.databind.DeserializationContext import com.fasterxml.jackson.databind.JsonDeserializer import com.fasterxml.jackson.databind.JsonNode class FooDeserializer : JsonDeserializer<Foo>() { override fun deserialize(p: JsonParser, ctxt: DeserializationContext): Foo { val node: JsonNode = p.codec.readTree(p) return if (node.isTextual) { // 字符串类型直接作为bar的值 Foo(node.asText()) } else { // 对象类型解析内部的bar字段 Foo(node.get("bar").asText()) } } }
3. 绑定反序列化器到目标字段
在Hello类的foo字段上添加@JsonDeserialize注解,指定使用自定义反序列化器:
import com.fasterxml.jackson.databind.annotation.JsonDeserialize data class Hello( @JsonDeserialize(using = FooDeserializer::class) val foo: Foo )
4. 测试反序列化
现在可以正常解析两种格式的JSON了:
val json = """ { "hello": { "foo": "string" } } """.trimIndent() val json2 = """ { "hello": { "foo": { "bar": "string" } } } """.trimIndent() val mapper = jacksonObjectMapper() val hello = mapper.readValue(json, MyJson::class.java) val hello2 = mapper.readValue(json2, MyJson::class.java) // 验证结果 println(hello.hello.foo.bar) // 输出 "string" println(hello2.hello.foo.bar) // 输出 "string"
简化方案:用JsonNode临时接收
如果不想写自定义反序列化器,也可以先把foo声明为JsonNode类型,解析后手动判断转换:
data class Hello(val foo: JsonNode) // 解析后处理逻辑 val hello = mapper.readValue(json, MyJson::class.java) val fooBar = if (hello.hello.foo.isTextual) { hello.hello.foo.asText() } else { hello.hello.foo.get("bar").asText() }
这种方式更灵活,适合简单场景,但需要手动处理类型转换逻辑。
内容的提问来源于stack exchange,提问作者Danny
相关产品推荐
相关产品推荐

