【问题标题】:How do I deserialize JSON into a List<SomeType> with Kotlin + Jackson [duplicate]如何使用 Kotlin + Jackson 将 JSON 反序列化为 List<SomeType> [重复]
【发布时间】:2016-04-15 11:26:03
【问题描述】:

反序列化以下 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

【问题讨论】:

    标签: json jackson kotlin


    【解决方案1】:

    注意: @IRus 的回答也是正确的,在我写这篇文章的同时正在修改它以填写更多细节。 em>

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

    您的第一个代码示例:

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

    返回推断类型 List&lt;*&gt;,因为您没有指定更多类型信息,它实际上是 List&lt;Map&lt;String,Any&gt;&gt;,它并不是真正“工作正常”,但不会产生任何错误。它是不安全的,没有输入。

    第二个代码应该是:

    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 的扩展功能和附加模块。它很小,并且解决了其他需要您跳过箍来创建默认构造函数或使用一堆注释的问题。有了这个模块,你的类就可以是普通的 Kotlin(可选为 data 类):

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

    【讨论】:

    • 我正在为 Jackson 使用 Kotlin 模块,并尝试过类似的方法,但忘记了导入。
    • 我希望 youtrack.jetbrains.com/issue/KT-9238 允许库告诉 IDE 哪些导入对它很重要。
    • 我刚刚意识到这不适用于泛型。 mapper.readValue>(json) 将生成哈希图列表。我明白为什么,但我似乎无法找到解决方法......
    • @UncleBob 这是对具体化泛型的限制。 &lt;T&gt; 是否来自本地类或函数,它是如何声明的?您在这里有一个特殊情况,并且可能值得发布一个新问题,该问题专门针对在您对函数的调用中使用嵌套范围的通用参数。
    【解决方案2】:

    你得到的错误是关于以下表达式:

    List<GenreDTO>::class.java
    

    由于 jvm 处理泛型的方式,List&lt;GenreDTO&gt; 没有单独的类,因此编译器会抱怨。同样,在 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。考虑查看Extensions 以了解详细信息。

    【讨论】:

    【解决方案3】:

    以下代码对我很有效:

    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)
    

    【讨论】:

    • 此代码不正确。它仅适用于您的数据类型是数组。当涉及泛型时,使用::class.java 总是错误的。
    • 谢谢@JaysonMinard,我发现了以前没有注意到的扩展功能。
    • 如果我的json包含字段对象,我应该如何自定义?
    • “字段对象”是什么意思,您喜欢自定义什么?
    • @IRus 这是魔术代码'registerKotlinModule()'
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-01-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-28
    • 2021-11-14
    相关资源
    最近更新 更多