Hello, I'm using kotlinx serialization. I want to ...
# serialization
n
Hello, I'm using kotlinx serialization. I want to report cases where a fallback to the default value occurs due to receiving strange values from the server instead of primitive values. However, it seems that serialization does not provide a callback for such cases. Therefore, I'm considering solving this by creating a custom serializer and annotating each primitive field with @Contextual. Below is the approximate code:
Copy code
object DefaultIntSerializer : KSerializer<Int> {
    override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("DefaultInt", <http://PrimitiveKind.INT|PrimitiveKind.INT>)

    override fun serialize(encoder: Encoder, value: Int) {
        encoder.encodeInt(value)
    }

    override fun deserialize(decoder: Decoder): Int {
        if (decoder is JsonDecoder) {
            val jsonElement = decoder.decodeJsonElement()
            return runCatching {
                when (jsonElement) {
                    is JsonPrimitive -> {
                        if (jsonElement.isString) {
                            jsonElement.content.toInt()
                        } else {
                            <http://jsonElement.int|jsonElement.int>
                        }
                    }

                    else -> throw SerializationException("Unexpected JSON token: $jsonElement")
                }
            }.onFailure {
                println("Warning: Default value used for Int field due to error: ${it.message}")
            }.getOrDefault(0)
        } else {
            throw SerializationException("Expected JsonDecoder for DefaultIntSerializer")
        }
    }
}

val DefaultReportingModule = SerializersModule {
    contextual(String::class, DefaultStringSerializer)
    contextual(Int::class, DefaultIntSerializer)
    contextual(Boolean::class, DefaultBooleanSerializer)
    contextual(Double::class, DefaultDoubleSerializer)
}
However, annotating every model property with @Contextual seems tedious, so I'm thinking of using a compiler plugin to automatically add it to the properties of specific classes. Are there any problems with my approach? If so, can you recommend an alternative way to solve this problem?
The K2 compiler plugin doesn't have enough references, so it's really difficult for me. 😭
To summarize what I want: When strange values come from the server, I want to fall back to the desired default values, but since this indicates a problem, I'd like it to be reported.
g
KSP is a good tool if you need to generate code but doesn't have time to learn the K2 compiler intricacies. Unfortunately you'll not be able to modify existing source code with KSP, only generate new code. I'm not sure there's a settings in JsonConfiguration to handle your needs, ignoreUnknownKeys may help you but I don't see how to make it work with your needs. If I was you, I'd look at a custom linter to ensure @Contextual is setup everywhere (and even better ask your backend team to clean the serialization on their side). Good luck
n
Thank you very much for your response. It seems that my understanding of the situation is correct. So, if we assume there is enough time, is it possible to create a compiler plugin that adds contextual properties to a specific package's class properties? If it is indeed possible, even if challenging, I would like to give it a try. Challenging tasks are fun, and they might also help improve my skills!
g
I never tried. Note that compiler plugin may also imply long-term maintenance cost, potentially breaking with the Kotlin upgrades of your project (I'm not sure about compiler internal stability...).
👍 1
😭 1
e
in this case I think it would be easier to create a custom format wrapping Json than to work around every serializer
n
@ephemient Could you please explain in more detail? It seems that the relevant logic is deeply embedded, making it difficult to wrap with Json. I would be very grateful if you could provide some hints.
e
totally untested but something like this should let you
Copy code
val json = Json { ... }
IgnoreTypeErrorsFormat(json).decodeFromString<T>(...)
and behave similarly to
json.decodeFromString(...)
except that you get nulls or 0's instead of type mismatches
today i learned 2
n
@ephemient Oh did you write this code yourself? It looks like it's worth studying! I'll build on it and see if it does what I want it to do and let you know!
e
I've written a couple other custom formats, and this was partly based off of one of them. serializers that depend on the exact type of the format (JsonContentPolymorphicSerializer, JsonTransformingSerializer) might not work with it, and it's missing some other Json features (@JsonNames), but if you're not using those then an approach like this should work
👀 1
👍 1