我正在尝试反序列化这样的 JSON(更复杂,但这是重要的部分):
[
{
"field": "field1",
"value": [1000, 2000]
},
{
"field": "field2",
"value": 1
},
{
"field": "field2",
"value":["strval2","strval3"]
},
{
"field": "field4",
"value": "strval1"
}
]
我试图弄清楚如何在不同的变体中使用 JsonContentPolymorphicSerializer 但结果都是一样的: 类 java.util.ArrayList 无法转换为类 myorg.ConditionValue (java.util.ArrayList 位于加载器“bootstrap”的 java.base 模块中;myorg.ConditionValue 位于加载器“app”的未命名模块中)嗯>
@Serializable
sealed class ConditionValue
@Serializable(with = StringValueSerializer::class)
data class StringValue(val value: String) : ConditionValue()
@Serializable(with = StringListValueSerializer::class)
data class StringListValue(val value: List<StringValue>) : ConditionValue()
object ConditionSerializer : JsonContentPolymorphicSerializer<Any>(Any::class) {
override fun selectDeserializer(element: JsonElement) = when (element) {
is JsonPrimitive -> StringValueSerializer
is JsonArray -> ListSerializer(StringValueSerializer)
else -> StringValueSerializer
}
}
object StringValueSerializer : KSerializer<StringValue> {
override val descriptor: SerialDescriptor = buildClassSerialDescriptor("StringValue")
override fun deserialize(decoder: Decoder): StringValue {
require(decoder is JsonDecoder)
val element = decoder.decodeJsonElement()
return StringValue(element.jsonPrimitive.content)
}
override fun serialize(encoder: Encoder, value: StringValue) {
encoder.encodeString(value.value)
}
}
我错过了什么?以及如何处理它?</p>
最佳答案
这确实是一个难题。
可能最快、最清晰的方法是避免陷入“正确”Kotlinx 序列化器方式的困境,而只需将多态类型解码为 JsonElement
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonElement
@Serializable
data class MyData(
val field: String,
val value: JsonElement, // polymorphism is hard, JsonElement is easy
)
以下代码产生正确的输出
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
fun main() {
val json = /*language=json*/ """
[
{
"field": "field1",
"value": [1000, 2000]
},
{
"field": "field2",
"value": 1
},
{
"field": "field2",
"value":["strval2","strval3"]
},
{
"field": "field4",
"value": "strval1"
}
]
""".trimIndent()
val result = Json.decodeFromString<List<MyData>>(json)
println(result)
}
[
MyData(field=field1, value=[1000,2000]),
MyData(field=field2, value=1),
MyData(field=field2, value=["strval2","strval3"]),
MyData(field=field4, value="strval1")
]
现在您可以手动将 MyData
转换为更正确的实例。
val converted = results.map { result ->
val convertedValue: ConditionValue = when (val value = result.value) {
is JsonPrimitive -> convertPrimitive(value)
is JsonArray -> convertJsonArray(value)
else -> error("cannot convert $value")
}
MyDataConverted(
field = result.field,
value = convertedValue
)
}
...
fun convertJsonArray(array: JsonArray): ConditionValueList<*> =
TODO()
fun convertPrimitive(primitive: JsonPrimitive): ConditionValuePrimitive =
TODO()
最后一点,我建议使用 inline classes来代表你的值(value)观。如果您确实想使用 Kotlinx 序列化,then they work better而不是为原始类型创建自定义序列化器。
以下是我在您的示例中对数据进行建模的方式:
sealed interface ConditionValue
sealed interface ConditionValuePrimitive : ConditionValue
sealed interface ConditionValueCollection<T : ConditionValuePrimitive> : ConditionValue
@JvmInline
value class StringValue(val value: String) : ConditionValuePrimitive
@JvmInline
value class IntegerValue(val value: Int) : ConditionValuePrimitive
@JvmInline
value class ConditionValueList<T : ConditionValuePrimitive>(
val value: List<T>
) : ConditionValueCollection<T>
data class MyDataConverted(
val field: String,
val value: ConditionValue,
)
版本:
- Kotlin 1.7.10
- Kotlinx 序列化 1.3.3
关于json - kotlinx反序列化: different types && scalar && arrays,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72813812/