如何使用 jackson 反序列化为 Kotlin 集合
How to use jackson to deserialize to Kotlin collections
我想要的示例代码:
data class D(val a: String, val b: Int)
val jsonStr = """[{"a": "value1", "b": 1}, {"a": "value2", "b":"2}]"""
// what I need
val listOfD: List<D> = jacksonObjectMapper().whatMethodAndParameter?
TL;DR
Jackson 2.6.3-2
按照@jason-minard 的建议做,简单地使用:
import com.fasterxml.jackson.module.kotlin.readValue
val listOfD: List<D> = jacksonMapper.readValue(jsonStr)
详情
Kotlin 没有什么特别之处 deserializing collections, although you'll need the kotlin jackson module 反序列化数据 类 没有注释。
也就是说,在您的情况下,它需要完整的具体化类型信息,以便跟踪列表的通用参数 (D
);否则(例如,如果您使用 readValue(jsonStr, List::class.java)
)Jackson 只会将其视为已擦除类型(即 List
)(正如 Kotlin 明确指出的那样)并将其反序列化为 List<Map<String, String>>
,而不知道它具有构造 D
s。这是 worked around 通过在 Java 中使用 TypeReference
的匿名子类,以便 Jackson 可以在 运行 时间访问完整的具体化类型以反序列化。
将 Jackson Java 代码从字面上翻译成 Kotlin,以下实现了您想要的(正如@eski 评论的那样,请注意示例中的 JSON 是无效的):
val jacksonMapper = ObjectMapper().registerModule(KotlinModule())
val jsonStr = """[{"a": "value1", "b": 1}, {"a": "value2", "b":2}]"""
val listOfD: List<D> = jacksonMapper.readValue(jsonStr, object : TypeReference<List<D>>(){})
assertEquals(listOf(D("value1", 1), D("value2", 2)), listOfD)
虽然这有点冗长和丑陋,因此您可以将它隐藏在 Kotlin(扩展)函数中(特别是如果您计划多次使用它):
inline fun <reified T> ObjectMapper.readValue(json: String): T = readValue(json, object : TypeReference<T>(){})
这样您就可以调用:
val listOfD: List<D> = jacksonMapper.readValue(jsonStr)
这正是 2.6.3-2
jackson Kotlin 模块中包含的内容。
使用 Jackson Kotlin 模块 current versions,如果您导入完整的模块包或特定的扩展函数,您将拥有所有可用的扩展方法。如:
import com.fasterxml.jackson.module.kotlin.*
val JSON = jacksonObjectMapper() // keep around and re-use
val myList: List<String> = JSON.readValue("""["a","b","c"]""")
因此,Kotlin 的 Jackson 模块将推断出正确的类型,您不需要 TypeReference
实例。
所以你的情况(稍微重命名并修复了数据 class 和 JSON):
import com.fasterxml.jackson.module.kotlin.readValue
data class MyData(val a: String, val b: Int)
val JSON = jacksonObjectMapper()
val jsonStr = """[{"a": "value1", "b": 1}, {"a": "value2", "b": 2}]"""
val myList: List<MyData> = JSON.readValue(jsonStr)
您还可以使用以下形式:
val myList = JSON.readValue<List<MyData>>(jsonStr)
如果没有导入,您将遇到错误,因为找不到扩展函数。
如果您没有通用内容的类型,例如下面示例中的 <MyData>
:
val value = context.readValue<List<MyData>>(parser, valueType)
您不仅可以实现 JsonSerialzier
,还可以实现下面代码示例中的 ContextualDeserializer
(基于 Java 中的 ):
class GenericListDeserializer : JsonDeserializer<List<*>>(), ContextualDeserializer {
private var valueType: JavaType? = null
override fun createContextual(ctxt: DeserializationContext, property: BeanProperty): JsonDeserializer<List<*>> {
val wrapperType = property.type
val valueType = wrapperType.containedType(0)
val deserializer = GenericListDeserializer()
deserializer.valueType = valueType
return deserializer
}
override fun deserialize(parser: JsonParser, context: DeserializationContext): List<*> {
val value :Any? = context.readValue(parser, valueType)
return listOf(value)
}
}
我想要的示例代码:
data class D(val a: String, val b: Int)
val jsonStr = """[{"a": "value1", "b": 1}, {"a": "value2", "b":"2}]"""
// what I need
val listOfD: List<D> = jacksonObjectMapper().whatMethodAndParameter?
TL;DR
Jackson 2.6.3-2
按照@jason-minard 的建议做,简单地使用:
import com.fasterxml.jackson.module.kotlin.readValue
val listOfD: List<D> = jacksonMapper.readValue(jsonStr)
详情
Kotlin 没有什么特别之处 deserializing collections, although you'll need the kotlin jackson module 反序列化数据 类 没有注释。
也就是说,在您的情况下,它需要完整的具体化类型信息,以便跟踪列表的通用参数 (D
);否则(例如,如果您使用 readValue(jsonStr, List::class.java)
)Jackson 只会将其视为已擦除类型(即 List
)(正如 Kotlin 明确指出的那样)并将其反序列化为 List<Map<String, String>>
,而不知道它具有构造 D
s。这是 worked around 通过在 Java 中使用 TypeReference
的匿名子类,以便 Jackson 可以在 运行 时间访问完整的具体化类型以反序列化。
将 Jackson Java 代码从字面上翻译成 Kotlin,以下实现了您想要的(正如@eski 评论的那样,请注意示例中的 JSON 是无效的):
val jacksonMapper = ObjectMapper().registerModule(KotlinModule())
val jsonStr = """[{"a": "value1", "b": 1}, {"a": "value2", "b":2}]"""
val listOfD: List<D> = jacksonMapper.readValue(jsonStr, object : TypeReference<List<D>>(){})
assertEquals(listOf(D("value1", 1), D("value2", 2)), listOfD)
虽然这有点冗长和丑陋,因此您可以将它隐藏在 Kotlin(扩展)函数中(特别是如果您计划多次使用它):
inline fun <reified T> ObjectMapper.readValue(json: String): T = readValue(json, object : TypeReference<T>(){})
这样您就可以调用:
val listOfD: List<D> = jacksonMapper.readValue(jsonStr)
这正是 2.6.3-2
jackson Kotlin 模块中包含的内容。
使用 Jackson Kotlin 模块 current versions,如果您导入完整的模块包或特定的扩展函数,您将拥有所有可用的扩展方法。如:
import com.fasterxml.jackson.module.kotlin.*
val JSON = jacksonObjectMapper() // keep around and re-use
val myList: List<String> = JSON.readValue("""["a","b","c"]""")
因此,Kotlin 的 Jackson 模块将推断出正确的类型,您不需要 TypeReference
实例。
所以你的情况(稍微重命名并修复了数据 class 和 JSON):
import com.fasterxml.jackson.module.kotlin.readValue
data class MyData(val a: String, val b: Int)
val JSON = jacksonObjectMapper()
val jsonStr = """[{"a": "value1", "b": 1}, {"a": "value2", "b": 2}]"""
val myList: List<MyData> = JSON.readValue(jsonStr)
您还可以使用以下形式:
val myList = JSON.readValue<List<MyData>>(jsonStr)
如果没有导入,您将遇到错误,因为找不到扩展函数。
如果您没有通用内容的类型,例如下面示例中的 <MyData>
:
val value = context.readValue<List<MyData>>(parser, valueType)
您不仅可以实现 JsonSerialzier
,还可以实现下面代码示例中的 ContextualDeserializer
(基于 Java 中的
class GenericListDeserializer : JsonDeserializer<List<*>>(), ContextualDeserializer {
private var valueType: JavaType? = null
override fun createContextual(ctxt: DeserializationContext, property: BeanProperty): JsonDeserializer<List<*>> {
val wrapperType = property.type
val valueType = wrapperType.containedType(0)
val deserializer = GenericListDeserializer()
deserializer.valueType = valueType
return deserializer
}
override fun deserialize(parser: JsonParser, context: DeserializationContext): List<*> {
val value :Any? = context.readValue(parser, valueType)
return listOf(value)
}
}