web-dev-qa-db-ja.com

jacksonを使用してKotlinコレクションにデシリアライズする方法

私が欲しいサンプルコード:

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?
16
Marvin

Jackson Kotlin Module 現在のバージョン を使用すると、完全なモジュールパッケージまたは特定の拡張機能をインポートすると、すべての拡張メソッドが使用可能になります。といった:

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インスタンスは必要ありません。

あなたのケース(わずかに名前を変更してデータクラスと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)

拡張機能が見つからないため、インポートしないとエラーが発生します。

29
Jayson Minard

TL; DR

ジャクソンで_2.6.3-2_ @ jason-minardが推奨するように実行し、単に使用します:

_import com.fasterxml.jackson.module.kotlin.readValue

val listOfD: List<D> = jacksonMapper.readValue(jsonStr)
_

詳細

コレクションの逆シリアル化 に対するKotlinの特別なことはありませんが、注釈なしでデータクラスを逆シリアル化するには kotlin jacksonモジュール が必要です。

つまり、リスト(D)のジェネリックパラメーターを追跡するために、完全に具体化された型情報が必要になります。そうでない場合(たとえばreadValue(jsonStr, List::class.Java)を使用する場合)、ジャクソンは消去されたタイプ(つまりList)としてのみ(Kotlinが明示的に示すように)、それを_List<Map<String, String>>_にデシリアライズしますが、 Dsを構築する必要があることを知っています。これは 回避策 JavaのTypeReferenceの匿名サブクラスを使用することにより、ジャクソンが実行時にデシリアライズする完全な型にアクセスできるようにします。

ジャクソン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モジュールに含まれているものです。

14
desseim

以下の例の<MyData>のような汎用コンテンツのタイプがない場合:

val value = context.readValue<List<MyData>>(parser, valueType)

以下のコードサンプルのようにJsonSerialzierだけでなくContextualDeserializerを実装することで作成できます(これはJavaの answer に基づいています)。

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)
    }
}
0