我正在学习如何使用Ktor的HttpClient。而且,我希望它能自动将JSON响应转换为数据类。我想我的所有设置都是正确的(粘贴在下面(,但不幸的是,java.time.Instant
使用了import java.io.Serializable;
,我想这与kotlinx的@kotlinx.serialization.Serializable
不兼容。
那么,如何让Ktor将Instant
识别为可序列化的呢?
val httpClient = HttpClient(CIO) {
install(JsonFeature) {
serializer = KotlinxSerializer(Json {
prettyPrint = true
isLenient = true
})
}
}
val response: MyResponse = httpClient.get(baseUrl() + "/example/path") {
contentType(ContentType.Application.Json)
}
@kotlinx.serialization.Serializable
data class MyResponse(
val name: String,
val time: Instant // ERROR: "Serializer has not been found for type 'Instant'. To use context serializer as fallback, explicitly annotate type or property with @Contextual"
)
旁注:使用Gson或Jackson或其他序列化程序的其他答案也可能很有用,因为它们可能不必显式添加@Serializable
如果您希望或需要直接使用java.time
,另一个解决方案是为java.time.Instant
创建自己的序列化程序。请参见以下示例。注Kotlin 1.9.0、Kotlin系列1.5.1和Gradle 8.2.1用于测试。
InstantSerializer.kt:
package sample
import kotlinx.serialization.KSerializer
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import java.time.Instant
object InstantSerializer : KSerializer<Instant> {
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("java.time.Instant", PrimitiveKind.STRING)
override fun serialize(encoder: Encoder, value: Instant) = encoder.encodeString(value.toString())
override fun deserialize(decoder: Decoder): Instant = Instant.parse(decoder.decodeString())
}
注意:ISO-8601表示的编码和解码可确保不丢失精度。这是kotlinx-datetime库使用的方法之一(请参阅此处(。库提供的另一种方法是分别将秒的历元和纳米编码为数字。如果您喜欢,请查看库的实现(前面链接(。还要注意,在JVM上运行时,kotlinx-datetime类由java.time
类支持
Event.kt(具有Instant
属性的可序列化数据类(:
@file:UseSerializers(InstantSerializer::class)
package sample
import kotlinx.serialization.Serializable
import kotlinx.serialization.UseSerializers
import java.time.Instant
@Serializable
data class Event(val name: String, val instant: Instant)
注意:使用@UseSerializers
的替代方法是用@Serializable(InstantSerializer::class)
注释instant
属性
Main.kt:
package sample
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import java.time.Instant
fun main() {
val event = Event("Test Event", Instant.now())
val jsonString = Json.encodeToString(event)
val decodedEvent = Json.decodeFromString<Event>(jsonString)
println("Original Event = $event")
println("JSON String = $jsonString")
println("Decoded Event = $decodedEvent")
}
示例输出:
Original Event = Event(name=Test Event, instant=2023-07-11T10:16:34.742769200Z)
JSON String = {"name":"Test Event","instant":"2023-07-11T10:16:34.742769200Z"}
Decoded Event = Event(name=Test Event, instant=2023-07-11T10:16:34.742769200Z)
这是用于运行上述代码的Gradle构建文件(Kotlin DSL(:
plugins {
kotlin("jvm") version "1.9.0"
kotlin("plugin.serialization") version "1.9.0"
application
}
application {
mainClass.set("sample.MainKt")
}
repositories {
mavenCentral()
}
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1")
}
它是用执行的
./gradlew run
一个解决方案是使用kotlinx日期时间库(https://github.com/Kotlin/kotlinx-datetime),它为您提供了您正在寻找的带有@kotlinx.serialization.Serializable
的Kotlin版本的Instant。
因此,您可以在MyResponse中使用kotlinx.datetime.Instant
,而不是使用java.time.Instant
。
请注意,该库是实验性的,API可能会更改。(来源:https://github.com/Kotlin/kotlinx-datetime#using-在您的项目中(