没有 @Serializable 的数据 class 的自定义序列化程序

Custom serializer for data class without @Serializable

我正在尝试将 JSON 文件反序列化为 Kotlin 数据 class 我无法使用 kotlinx.serialization.

进行控制

class 看起来类似于:

public data class Lesson(
    val uid: String,
    val start: Instant,
    val end: Instant,
    val module: String,
    val lecturers: List<String>,
    val room: String?,
    val type: String?,
    val note: String?
)

我尝试解析的 JSON 看起来像这样:

{
  "lessons": [
    {
      "uid": "sked.de956040",
      "start": "2020-11-02T13:30:00Z",
      "end": "2020-11-02T16:45:00Z",
      "module": "IT2101-Labor SWE I: Gruppe 1 + 2",
      "lecturers": [
        "Kretzmer"
      ],
      "room": "-",
      "type": "La",
      "note": "Prüfung Online"
    }
  ]
}

这是通过以下方式尝试的:

@Serializable
data class ExpectedLessons(
    val lessons: List<Lesson>
)

val decoded = Json.decodeFromString<ExpectedLessons>(text)

由于无法修改 class Lesson,因此无法添加 @Serializable 注释来进行(反)序列化。 因此,您可以创建两个自定义序列化器来使其工作。

@OptIn(ExperimentalSerializationApi::class)
@Serializer(forClass = Lesson::class)
object LessonSerializer : KSerializer<Lesson> {
    override val descriptor: SerialDescriptor = buildClassSerialDescriptor("Lesson") {
        element<String>("uid")
        element<String>("start")
        element<String>("end")
        element<String>("module")
        element<List<String>>("lecturers")
        element<String?>("room", isOptional = true)
        element<String?>("type", isOptional = true)
        element<String?>("note", isOptional = true)
    }

    override fun serialize(encoder: Encoder, value: Lesson) {
        encoder.encodeStructure(descriptor) {
            encodeStringElement(descriptor, 0, value.uid)
            encodeSerializableElement(descriptor, 1, InstantSerializer, value.start)
            encodeSerializableElement(descriptor, 2, InstantSerializer, value.end)
            encodeStringElement(descriptor, 3, value.module)
            encodeSerializableElement(descriptor, 4, ListSerializer(String.serializer()), value.lecturers)
            encodeNullableSerializableElement(descriptor, 5, String.serializer(), value.room)
            encodeNullableSerializableElement(descriptor, 6, String.serializer(), value.type)
            encodeNullableSerializableElement(descriptor, 7, String.serializer(), value.note)
        }
    }

    override fun deserialize(decoder: Decoder): Lesson {
        return decoder.decodeStructure(descriptor) {
            var uid: String? = null
            var start: Instant? = null
            var end: Instant? = null
            var module: String? = null
            var lecturers: List<String> = emptyList()
            var room: String? = null
            var type: String? = null
            var note: String? = null

            loop@ while (true) {
                when (val index = decodeElementIndex(descriptor)) {
                    DECODE_DONE -> break@loop

                    0 -> uid = decodeStringElement(descriptor, 0)
                    1 -> start = decodeSerializableElement(descriptor, 1, InstantSerializer)
                    2 -> end = decodeSerializableElement(descriptor, 2, InstantSerializer)
                    3 -> module = decodeStringElement(descriptor, 3)
                    4 -> lecturers = decodeSerializableElement(descriptor, 4, ListSerializer(String.serializer()))
                    5 -> room = decodeNullableSerializableElement(descriptor, 5, String.serializer().nullable)
                    6 -> type = decodeNullableSerializableElement(descriptor, 6, String.serializer().nullable)
                    7 -> note = decodeNullableSerializableElement(descriptor, 7, String.serializer().nullable)

                    else -> throw SerializationException("Unexpected index $index")
                }
            }

            Lesson(
                requireNotNull(uid),
                requireNotNull(start),
                requireNotNull(end),
                requireNotNull(module),
                lecturers,
                room,
                type,
                note
            )
        }
    }
}


@OptIn(ExperimentalSerializationApi::class)
@Serializer(forClass = Instant::class)
object InstantSerializer : KSerializer<Instant> {
    override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Instant", PrimitiveKind.STRING)

    override fun serialize(encoder: Encoder, value: Instant) {
        encoder.encodeString("$value")
    }

    override fun deserialize(decoder: Decoder): Instant {
        return Instant.parse(decoder.decodeString())
    }
}

您可以像这样在使用序列化程序之前配置它们:

@file:UseSerializers(InstantSerializer::class, LessonSerializer::class)