Ktor:在多平台中使用 List 作为根目录序列化/反序列化 JSON



我们如何将kotlin.serialize与Ktor的HttpClient一起使用,以列表作为根来反序列化/序列化JSON? 我正在创建HttpClient,如下所示:

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

看来我需要为列表设置映射器,但是泛型是不可能的。 我看到我可以使用MyClass.serializer((.list获取它的序列化程序,但是注册它以在http请求上反序列化/序列化并不简单。 有人知道一个好的解决方案吗?

您可以编写包装器和自定义序列化程序:

@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
}

更新 ktor 1.3.0:

现在,您可以直接从客户端接收默认集合(例如列表(:

@Serializable
data class User(val id: Int)
val response: List<User> = client.get(...)
// or client.get<List<User>>(...)

在 ktor 1.3.0 之前:

目前还没有办法在 kotlinx.serialization 中(反(序列化这样的 JSON。

对于序列化,您可以尝试如下操作:

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

并且没有已知的方法来获取列表反序列化程序。

这更像是一种解决方法,但在逐步完成KotlinxSerializer代码后,我看不到任何其他方法。 例如,如果您查看KotlinxSerializer.read(),您会发现它尝试根据type查找mapper,但在这种情况下,它只是一个kotlin.collections.List并且无法解决。 我试过调用类似setListMapper(MyClass::class, MyClass.serializer())的东西,但这仅适用于序列化(通过write中的lookupSerializerByData方法使用(

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

我碰巧在 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()

希望这对:)有所帮助

最新更新