我正在尝试编写自定义序列化程序,因为我需要将对象反序列化为字符串。对象的反序列化正确完成,但是当我尝试反序列化简单的字符串时,出现异常:
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
如果我理解正确的话,你的 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()
}
}