Pat*_*ick 5 serialization json kotlin ktor kotlin-multiplatform
我们如何将kotlin.serialize与Ktor的HttpClient结合使用以反序列化/序列化以列表为根的JSON?我正在创建HttpClient,如下所示:
HttpClient {
install(JsonFeature) {
serializer = KotlinxSerializer().apply {
setMapper(MyClass::class, MyClass.serializer())
setMapper(AnotherClass::class, AnotherClass.serializer())
}
}
install(ExpectSuccess)
}
Run Code Online (Sandbox Code Playgroud)
看来我需要setMapper for List,但是使用泛型是不可能的。我看到我可以使用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))
}
}
}
Run Code Online (Sandbox Code Playgroud)
注册:
HttpClient {
install(JsonFeature) {
serializer = KotlinxSerializer().apply {
setMapper(MyClassList::class, MyClassList.serializer())
}
}
}
Run Code Online (Sandbox Code Playgroud)
并使用:
suspend fun fetchItems(): List<MyClass> {
return client.get<MyClassList>(URL).items
}
Run Code Online (Sandbox Code Playgroud)
使用 ktor 1.3.0 更新:
现在您可以直接从客户端接收默认集合(如列表):
@Serializable
data class User(val id: Int)
val response: List<User> = client.get(...)
// or client.get<List<User>>(...)
Run Code Online (Sandbox Code Playgroud)
在 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()
Run Code Online (Sandbox Code Playgroud)
并且没有已知的方法来获取列表解串器。
这更像是一种解决方法,但在单步执行KotlinxSerializer
代码后,我看不到任何其他方法。KotlinxSerializer.read()
例如,如果您查看示例,您会看到它尝试查找 a mapper
,type
但在这种情况下,它只是 akotlin.collections.List
并且无法解析。我曾尝试调用类似的东西,setListMapper(MyClass::class, MyClass.serializer())
但这仅适用于序列化(通过lookupSerializerByData
方法 in 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)
}
Run Code Online (Sandbox Code Playgroud)
所以,我最终做的是(注意serializer().list
电话)
suspend fun fetchBusStops(): List<BusStop> {
val jsonArrayString = client.get<String> {
url("$baseUrl/stops.json")
}
return JSON.nonstrict.parse(BusStop.serializer().list, jsonArrayString)
}
Run Code Online (Sandbox Code Playgroud)
不理想,显然不使用JsonFeature
.