如何将@kotlin.serialization.Serializable与java.time.Instant一起使用



我正在学习如何使用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-在您的项目中(

最新更新