web-dev-qa-db-ja.com

Ktor:マルチプラットフォームでルートとしてリストを使用してJSONをシリアル化/逆シリアル化

KtorのHttpClientでkotlin.serializeを使用して、リストをルートとしてJSONを逆シリアル化/シリアル化するにはどうすればよいですか?私は次のようにHttpClientを作成しています:

HttpClient {
       install(JsonFeature) {
           serializer = KotlinxSerializer().apply {
               setMapper(MyClass::class, MyClass.serializer())
               setMapper(AnotherClass::class, AnotherClass.serializer())
           }
       }
       install(ExpectSuccess)
   }

ListにMapperを設定する必要があるようですが、ジェネリックでは不可能です。 MyClass.serializer()。listを使用してシリアライザーを取得できるようですが、httpリクエストでデシリアライズ/シリアライズするように登録するのは簡単ではありません。誰かが良い解決策を知っていますか?

7
Patrick

Kotlinx.serializationでそのようなJSONを(逆)シリアル化する方法はまだありません。

シリアル化の場合は、次のようなものを試すことができます。

fun serializer(data: Any) = if (data is List<*>) {
   if (data is EmptyList) String::class.serializer().list // any class with serializer

   data.first()::class.serializer().list
} else data.serializer()

また、リストデシリアライザーを取得する既知の方法はありません。

1
Leonid

ラッパーとカスタムシリアライザーを作成できます。

@Serializable
class MyClassList(
    val items: List<MyClass>
) {

    @Serializer(MyClassList::class)
    companion object : KSerializer<MyClassList> {

        override val descriptor = StringDescriptor.withName("MyClassList")

        override fun serialize(output: Encoder, obj: MyClassList) {
            MyClass.serializer().list.serialize(output, obj.items)
        }

        override fun deserialize(input: Decoder): MyClassList {
            return MyClassList(MyClass.serializer().list.deserialize(input))
        }
    }
}

登録する:

HttpClient {
    install(JsonFeature) {
        serializer = KotlinxSerializer().apply {
            setMapper(MyClassList::class, MyClassList.serializer())
        }
    }
}

そして使用:

suspend fun fetchItems(): List<MyClass> {
    return client.get<MyClassList>(URL).items
}
1

Kotlin/JSでも同じ問題が発生し、次のように修正できました。

private val client = HttpClient(Js) {
  install(JsonFeature) {
    serializer = KotlinxSerializer().apply {
      register(User.serializer().list)
    }
  }
}
...
private suspend fun fetchUsers(): Sequence<User> =
    client.get<List<User>> {
      url("$baseUrl/users")
    }.asSequence()

お役に立てれば :)

0
Naliwe

これはより回避策ですが、KotlinxSerializerコードをステップ実行した後、他の方法を見つけることができませんでした。たとえば、KotlinxSerializer.read()を見ると、mapperに基づいてtypeを検索しようとしていることがわかりますが、この場合は_kotlin.collections.List_であり、解決しません。 setListMapper(MyClass::class, MyClass.serializer())のようなものを呼び出そうとしましたが、これはシリアル化でのみ機能します(lookupSerializerByDatawriteメソッドで使用)

_override suspend fun read(type: TypeInfo, response: HttpResponse): Any {
    val mapper = lookupSerializerByType(type.type)
    val text = response.readText()

    @Suppress("UNCHECKED_CAST")
    return json.parse(mapper as KSerializer<Any>, text)
}
_

だから、私がやったことは次のようなものでした(serializer().list呼び出しに注意してください)

_suspend fun fetchBusStops(): List<BusStop> {
    val jsonArrayString = client.get<String> {
        url("$baseUrl/stops.json")
    }

    return JSON.nonstrict.parse(BusStop.serializer().list, jsonArrayString)
}
_

理想的ではなく、明らかにJsonFeatureを利用していません。

0
John O'Reilly