web-dev-qa-db-ja.com

Kotlin + Jacksonを使用してJSONをリスト<SomeType>にデシリアライズするにはどうすればよいですか

次の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

17
Jasper Blues

注:@ IRusからの答えも正しいです、それはそうでした詳細を記入するためにこれを書いたのと同時に修正されました。

Jackson + Kotlin module を使用する必要があります。そうしないと、デフォルトのコンストラクターがない場合にKotlinオブジェクトへの逆シリアル化で他の問題が発生します。

コードの最初のサンプル:

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

型情報をさらに指定しなかったため、List<*>の推定型を返します。実際にはList<Map<String,Any>>であり、実際には「正常に動作していません」が、エラーは発生していません。それは安全ではなく、入力されていません。

2番目のコードは次のとおりです。

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)
26
Jayson Minard

次のコードは私にとってうまく機能します:

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 genericsを使用)のために機能します。

 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)
5
Ruslan

表示されるエラーは、次の式に関するものです。

List<GenreDTO>::class.Java

Jvmがジェネリックを処理する方法のため、List<GenreDTO>の個別のクラスはないため、コンパイラーは文句を言います。同様に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 を見て詳細を調べてください。

5
miensol