Closed loseryc closed 1 month ago
You want to intermediated this through json trees. Have a look at JsonTransformingSerializer
the example is the opposite of your case.
You want to intermediated this through json trees. Have a look at
JsonTransformingSerializer
the example is the opposite of your case.
The field definition in this entity is List<String>
, but the value in Json is a String
, which is common in the project, so we don't want to perform custom parsing for each entity, but instead want to use a method like the one in the question. @pdvrieze
@loseryc The thing is that your old approach makes assumptions about how the parsing works, and that on parse failure the parser is going to be at the initial state. This is brittle at least, and not guaranteed to work. What does work is to take the approach taken by JsonTransformingSerializer (either use it directly or use your own variant). Basically the following should work:
class JsonOrListSerializer<T>(private val elementSerializer: KSerializer<T>): KSerializer<List<T>> {
private val listSerializer = ListSerializer(elementSerializer)
override val descriptor: SerialDescriptor = SerialDescriptor("JsonOrListSerializer", listSerializer.descriptor)
override fun deserialize(decoder: Decoder): List<T> {
if (decoder !is JsonDecoder) return listSerializer.deserialize(decoder)
val elem = decoder.decodeJsonElement()
return when (elem) {
is JsonArray -> decoder.json.decodeFromJsonElement(listSerializer, elem)
else -> listOf(decoder.json.decodeFromJsonElement(elementSerializer, elem))
}
}
override fun serialize(encoder: Encoder, value: List<T>) {
when {
// this is to show how this would work
encoder is JsonEncoder && value.size == 1 ->
encoder.encodeSerializableValue(elementSerializer, value.single())
else -> listSerializer.serialize(encoder, value)
}
}
}
Note that the serialization applies the "optimization" here just to show how that would work, not to suggest this is the best way to do it. As to the deserialization, I explicitly use the format itself again to decode the list/members (the elementSerializer
can be String.serializer()
). If instead you want to do it only for strings you can shortcut it using knowledge of "stringness".
@pdvrieze Can do something like this?
@pdvrieze Can do something like this?
That would work too (but would fail on other formats than Json).
The thing is that your old approach makes assumptions about how the parsing works, and that on parse failure the parser is going to be at the initial state. This is brittle at least, and not guaranteed to work.
@pdvrieze does that mean there has been some significant changes to the way parsing used to happen in version 1.5.1
and 1.6.0
?
I think we have an example for JsonTransformingSerializer
that does almost exactly what you want: https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/json.md#array-wrapping
Regarding exception safety: we do not provide any guarantees on internal Encoder/Decoder
state after the exception is thrown, it is stated in the docs (see 'Exception safety' section here: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-encoder/). So it may work in one version of kotlinx.serialization, but do not work in the other, since internal state handling may have been changed.
Describe the question
I have a data class, in which the
name
field is defined asList<String>
, but when parsing the Json string, sometimes the value corresponding to thename
in the Json is aString
instead of aList<String>
. In this case, we need to process the string into aList
with a length of 1. We did this beforeThis worked fine on version
kotlinx.serialization:1.5.1
, but since upgrading thekotlinx.serialization
version to1.6.0
, it no longer works properly, Is there any way to make it work normally?When use
kotlinx.serialization:1.5.1
When use
kotlinx.serialization:1.6.0
Environment
Kotlin version: 1.9.20 Library version: 1.6.0 Kotlin platforms: Android, JVM Gradle version: 8.10 Android Studio Koala | 2024.1.1