Ktor: Сериализация / десериализация JSON с List как root в Multiplatform - PullRequest
0 голосов
/ 24 октября 2018

Как мы можем использовать kotlin.serialize с Ktt's HttpClient для десериализации / сериализации JSON со списками от имени root?Я создаю HttpClient следующим образом:

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

Появляется, мне нужно установить Mapper для List, однако это не возможно с универсальными.Я вижу, что могу получить сериализатор для него с помощью MyClass.serializer (). List, но зарегистрировать его для десериализации / сериализации по запросам http не так просто.Кто-нибудь знает хорошее решение?

Ответы [ 4 ]

0 голосов
/ 01 июня 2019

У меня случилась такая же проблема на 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 голосов
/ 20 января 2019

Это скорее обходной путь, но после перехода по коду KotlinxSerializer я не смог найти другого пути его обхода.Например, если вы посмотрите на KotlinxSerializer.read(), то увидите, что он пытается найти mapper на основе type, но в данном случае это просто kotlin.collections.List и не разрешается.Я пытался вызвать что-то вроде setListMapper(MyClass::class, MyClass.serializer()), но это работает только для сериализации (используя метод lookupSerializerByData в write)

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 call)

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

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

Не идеально и, очевидно, не использует JsonFeature.

0 голосов
/ 15 февраля 2019

Вы можете написать упаковщик и пользовательский сериализатор:

@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
}
0 голосов
/ 27 октября 2018

Нет способа (де) сериализовать такой JSON в kotlinx.serialization.

Для сериализации вы можете попробовать что-то вроде этого:

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()

И нет известныхСпособы получения списка десериализатора.

...