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

Ano*_*age 6 serialization json kotlin

我正在学习如何使用 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"
)
Run Code Online (Sandbox Code Playgroud)

旁注:使用 Gson 或 Jackson 或其他序列化器的其他答案也可能很有用,因为它们可能不必显式添加@Serializable

Sla*_*law 11

如果您想要或需要继续java.time直接使用,另一个解决方案是为java.time.Instant. 请参阅以下示例。注意使用 Kotlin 1.9.0、Kotlin Serialization 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())
}
Run Code Online (Sandbox Code Playgroud)

Note: Encoding to, and decoding from, the ISO-8601 representation ensures no precision is lost. That's one of the approaches used by the kotlinx-datetime library (see here). The other approach that library offers is to encode the epoch seconds and nano-of-seconds separately, both as numbers. If you prefer that, look at the library's implementation (linked earlier). Also note that when running on the JVM, the kotlinx-datetime classes are backed by the java.time classes.

Event.kt (the serializable data class with an Instant property):

@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)
Run Code Online (Sandbox Code Playgroud)

Note: An alternative to using to using @UseSerializers would be to annotate the instant property with @Serializable(InstantSerializer::class).

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")
}
Run Code Online (Sandbox Code Playgroud)

Example output:

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)
Run Code Online (Sandbox Code Playgroud)

Here was the Gradle build file used to run the above code (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")
}
Run Code Online (Sandbox Code Playgroud)

And it was executed with:

./gradlew run
Run Code Online (Sandbox Code Playgroud)


Ano*_*age 10

一种解决方案是使用 kotlinx-datetime 库(https://github.com/Kotlin/kotlinx-datetime@kotlinx.serialization.Serializable ),它为您提供了具有您正在寻找的功能的 Kotlin 版本的 Instant 。

因此,java.time.Instant您可以kotlinx.datetime.Instant在 MyResponse 中使用,而不是使用 。

请注意,该库是实验性的,API 可能会发生变化。(来源:https ://github.com/Kotlin/kotlinx-datetime#using-in-your-projects )