如何使用 Kotlin + Jackson 将 JSON 反序列化为 List<SomeType>

How do I deserialize JSON into a List<SomeType> with Kotlin + Jackson

反序列化以下 JSON 的正确语法是什么:

[ {
  "id" : "1",
  "name" : "Blues"
}, {
  "id" : "0",
  "name" : "Rock"
} ]

我试过:

//Works OK
val dtos  = mapper.readValue(json, List::class.java)

但是我想要:

val dtos : List<GenreDTO>  = mapper.readValue(json, 
    List<GenreDTO>::class.java)

以上语法不正确并给出:only classes are allowed on the left hand side of a class literal

以下代码对我来说效果很好:

import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.readValue
import com.fasterxml.jackson.module.kotlin.registerKotlinModule



val json = """[ {
  "id" : "1",
  "name" : "Blues"
}, {
  "id" : "0",
  "name" : "Rock"
} ]"""

data class GenreDTO(val id: Int, val name: String)

val mapper = ObjectMapper().registerKotlinModule()

fun main(args: Array<String>) {
    val obj: List<GenreDTO> = mapper.readValue(json)
    obj.forEach {
        println(it)
    }
}

这项工作是因为 jackson-kotlin-module 中定义的扩展函数(使用 reified 泛型):

 public inline fun <reified T: Any> ObjectMapper.readValue(content: String): T = readValue(content, object: TypeReference<T>() {})

感谢@JaysonMinard 通知我。

输出:

GenreDTO(id=1, name=Blues)
GenreDTO(id=0, name=Rock)

您收到的错误与以下表达式有关:

List<GenreDTO>::class.java

由于 jvm 处理泛型的方式,List<GenreDTO> 没有单独的 class,因此编译器会抱怨。类似地,在 Java 中,以下内容不会编译:

List<GenreDTO>.getClass()

这是一个正确反序列化列表的示例:

val value:List<GenreDTO> = mapper.readValue(json, object : TypeReference<List<GenreDTO>>() {})

正如@JaysonMinard 指出的那样,您可以使用 jackson-module-kotlin 将调用简化为:

val genres: List<GenreDTO> = mapper.readValue(json)
// or
val genres = mapper.readValue<List<GenreDTO>>(json)

这是可能的,因为reified type parameters. Consider looking at Extensions要了解详情。

NOTE: @IRus 的回答也是对的,和我同时修改写这个是为了填写更多的细节。

您应该使用 Jackson + Kotlin module,否则当您没有默认构造函数时,在反序列化为 Kotlin 对象时会遇到其他问题。

您的第一个代码示例:

val dtos  = mapper.readValue(json, List::class.java)

正在返回推断类型 List<*> 因为您没有指定更多类型信息,它实际上是一个 List<Map<String,Any>> 而不是 "working OK" 但不会产生任何错误.不安全,不打字

第二个代码应该是:

import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.fasterxml.jackson.module.kotlin.readValue

val mapper = jacksonObjectMapper()
// ...
val genres: List<GenreDTO> = mapper.readValue(json)

作业右侧不需要任何其他内容,Jackson 的 Kotlin 模块将具体化泛型并在内部为 Jackson 创建 TypeReference。请注意 readValue 导入,您需要它或 .* 才能使 com.fasterxml.jackson.module.kotlin 包具有执行所有魔法的扩展功能。

一个略有不同但同样有效的替代方案:

val genres = mapper.readValue<List<GenreDTO>>(json)

没有理由不为 Jackson 使用扩展功能和 add-on 模块。它很小并且解决了其他问题,这些问题需要您跳过箍来制作默认构造函数或使用一堆注释。使用该模块,您的 class 可以是普通的 Kotlin(可选 data class):

class GenreDTO(val id: Int, val name: String)