使用自定义序列化器的 JsonDecodingException

问题描述 投票:0回答:1

我正在尝试编写自定义序列化程序,因为我需要将对象反序列化为字符串。对象的反序列化正确完成,但是当我尝试反序列化简单的字符串时,出现异常:

kotlinx.serialization.json.internal.JsonDecodingException: Unexpected JSON token at offset 10: Expected quotation mark '"', but had 't' instead at path: $.plan
JSON input: {"plan": "test"}

要序列化的对象

@Serializable
data class Test(
    val plan: PlanAsString?
)

序列化器

typealias PlanAsString = @Serializable(with = PlanSerializer::class) String

class PlanSerializer : KSerializer<String?> {
    override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("StringSerializer", PrimitiveKind.STRING)

    @OptIn(ExperimentalSerializationApi::class)
    override fun deserialize(decoder: Decoder): String? {
        kotlin.runCatching {
            val plan = decoder.decodeSerializableValue(Test.serializer())
            return plan.plan
        }.onFailure {
            return decoder.decodeString()
        }
        return decoder.decodeNull()
    }

    @OptIn(ExperimentalSerializationApi::class)
    override fun serialize(encoder: Encoder, value: String?) {
        return value?.let {
            encoder.encodeString(value)
        } ?: encoder.encodeNull()
    }
}

以及我尝试将字符串反序列化为对象的代码

Json.decodeFromString<Test>("{\"plan\": \"test\"}")  // This rows throws exception above
Json.decodeFromString<eu.abra.models.Test>("{\"plan\": {\"name\": \"basic\"}}") // This is deserialized correctly
kotlin serialization
1个回答
0
投票

如果我理解正确的话,你的 JSON 可以是

{"plan": "some name"}

{"plan": {"name": "some name"}}

并且您希望将两种格式序列化为

Test
对象,其中
plan
是字符串
"some name"

您的方法不起作用,因为解码器在无法解码预期对象时抛出异常后,它已经消耗了 JSON 字符串的第一个标记(

"
)。当您捕获异常并调用
decodeString
时,它将读取字符 after
"
,这不是它所期望的。


您可以使用

JsonContentPolymorphicSerializer
来完成此操作 - 根据遇到的 JSON 元素是 JSON 对象还是 JSON 字符串来选择正确的序列化程序。

object PlanSerializer : JsonContentPolymorphicSerializer<String>(String::class) {
    override fun selectDeserializer(element: JsonElement): DeserializationStrategy<String> =
        if (element is JsonPrimitive && element.isString) {
            String.serializer()
        } else {
            PlanAsStringSerializer
        }

    private object PlanAsStringSerializer : KSerializer<String> {
        override val descriptor: SerialDescriptor = Plan.serializer().descriptor
        override fun deserialize(decoder: Decoder): String =
            decoder.decodeSerializableValue(Plan.serializer()).name
        override fun serialize(encoder: Encoder, value: String) = error("Not supported")
    }
}

@Serializable
data class Plan(
    val name: String
)

如果您想在出现问题时退回到

null
,您可以解码
JsonElement

class PlanSerializer : KSerializer<String?> {
    override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("StringSerializer", PrimitiveKind.STRING)

    override fun deserialize(decoder: Decoder): String? {
        return runCatching {
            when(val plan = decoder.decodeSerializableValue(JsonElement.serializer())) {
                is JsonPrimitive -> plan.takeIf { it.isString }?.content
                is JsonObject -> Json.decodeFromJsonElement<Plan>(plan).name
                else -> null
            }
        }.getOrNull()
    }

    @OptIn(ExperimentalSerializationApi::class)
    override fun serialize(encoder: Encoder, value: String?) {
        return value?.let {
            encoder.encodeString(value)
        } ?: encoder.encodeNull()
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.