I have an arrow Either deserializer that I found i...
# serialization
s
I have an arrow Either deserializer that I found in a thread here, that I’ve slightly modified, but for some reason it fails if my object has a different class discriminator, even if I specify a specific one just for the deserializer. no clue what’s going on. anyone?
Copy code
class EitherSerializer<R>(
    private val leftSerializer: KSerializer<PricingError>,
    private val rightSerializer: KSerializer<R>,
) : KSerializer<Either<PricingError, R>> {
    override val descriptor: SerialDescriptor
        get() = rightSerializer.descriptor

    private val errorJson = Json {
        ignoreUnknownKeys = true
        isLenient = true
        encodeDefaults = true
    }

    override fun deserialize(decoder: Decoder): Either<PricingError, R> {
        require(decoder is JsonDecoder) { "only works in JSON format" }
        val element = Either.catch {
            decoder.decodeJsonElement()
        }.mapLeft { PricingError.SerializationError(it.localizedMessage) }
        return when (element) {
            is Either.Left -> element
            is Either.Right -> {
                try {
                    decoder.json.decodeFromJsonElement(rightSerializer, element.value).right()
                } catch (_: SerializationException) {
                    errorJson.decodeFromJsonElement(leftSerializer, element.value).left() // fails here with error in thread
                }
            }
        }
    }

    override fun serialize(encoder: Encoder, value: Either<PricingError, R>) {
        TODO("Not yet implemented")
    }
}
Copy code
2022-06-29 17:04:43,935 ERROR [io.qua.ama.lam.run.AbstractLambdaPollLoop] (Lambda Thread (DEVELOPMENT)) Failed to run lambda (DEVELOPMENT): kotlinx.serialization.json.internal.JsonDecodingException: Polymorphic serializer was not found for missing class discriminator ('null')
JSON input: {"errorType":"test","errorMessage":"response","version":"null:null"}
	at kotlinx.serialization.json.internal.JsonExceptionsKt.JsonDecodingException(JsonExceptions.kt:24)
	at kotlinx.serialization.json.internal.JsonExceptionsKt.JsonDecodingException(JsonExceptions.kt:32)
	at kotlinx.serialization.json.internal.PolymorphicKt.throwSerializerNotFound(Polymorphic.kt:76)
	at kotlinx.serialization.json.internal.PolymorphicKt.decodeSerializableValuePolymorphic(Polymorphic.kt:66)
	at kotlinx.serialization.json.internal.AbstractJsonTreeDecoder.decodeSerializableValue(TreeJsonDecoder.kt:51)
	at kotlinx.serialization.json.internal.TreeJsonDecoderKt.readJson(TreeJsonDecoder.kt:24)
	at kotlinx.serialization.json.Json.decodeFromJsonElement(Json.kt:119)
	at com.sunrun.pricing.flex.helpers.serialization.EitherSerializer.deserialize(EitherSerializer.kt:59)
	at com.sunrun.pricing.flex.helpers.serialization.EitherSerializer.deserialize(EitherSerializer.kt:35)
	at kotlinx.serialization.json.internal.PolymorphicKt.decodeSerializableValuePolymorphic(Polymorphic.kt:59)
	at kotlinx.serialization.json.internal.StreamingJsonDecoder.decodeSerializableValue(StreamingJsonDecoder.kt:38)
	at kotlinx.serialization.json.Json.decodeFromString(Json.kt:100)
Right serializer is
Copy code
object Serialization {
    val json: Json by lazy {
        Json {
            ignoreUnknownKeys = true
            isLenient = true
            classDiscriminator = "productType"
            encodeDefaults = true
            serializersModule = polySerializers
        }
    }
}
, left serializer is in the EitherSerializer
it works perfectly fine for the right element, because the class discriminator is there. fails for the left due to missing class discriminator. even if I use a different serializer.
@ephemient tagging you because your code is the one I based the above on.
The issue actually appears to be because kotlinx is trying to serialize my PricingError as a polymorphic class, which I actually do not want. I want to specify the exact type of error and have it serialize that without regard to the parent sealed class. I am using the hierarchy to control the Left value, not for polymorphism….
d
Your serializer is weird, I'm struggling to reason about what you're actually doing.
d
Why are you trying to catch the exception from
decodeJsonElement()
? That's going to lead to some seriously weird behaviour in more complex cases.
s
that’s the original serializer. I have an Either type, which means that I can be serializing one of two types. I catch the exception because without doing so the either is pointless. I want to map kotlinx serialization failures to types, rather than exception based programming.
d
I want to map kotlinx serialization failures to types
This is illegal. You cannot do this.
Do the "failures to type" outside the serialization process.
s
lol illegal? why?
d
Once the decoder throws, it is in a broken state. You cannot continue to use it.
e
yes, decoding errors are irrecoverable
d
Imagine trying to deserialize a list of these. You'd get some weird values later on.
s
that is exactly what the above code does. If it fails, it results in an Either.Left, which then immediately returns the mapped PricingError.
e
you cannot return a PricingError if the original decoder throws. the thing following your element will be in a totally broken state
with JSON, you can extract a JsonElement, and do a completely independent parse on that
s
you mean if this EitherSerializer is deserializing some nested value, correct? Not if the entire object is being decoded with the single eitherserializer
d
What do you actually want the JSON to look like? Do you have to conform to some schema or are you able to decide?
s
we’re not annotating individual types with this serializer. we’re using it to decode one of two things from rest services. either the service returns a complete json response, in which case the decoder should decode to the Right value (nothing to do with Either, just some static response type). Or it fails in some way, in which case the decoder should simply decode to some
PricingError
.
e
maybe you actually want
JsonContentPolymorphicSerializer
. hard to tell given the lack of detail
s
I’ve tried using that in the past with little luck, but I could try again.
d
Hmm, when it fails to you get an HTTP code that isn't 200 or something like that?
Maybe this isn't a serialization problem then.
s
it’s actually direct aws lambda calls, so no, there are no http codes
d
Ah, fair enough.
Seems like XY problem then.
s
essentially the system is like so. we have lambdas. they fire off calls to other lambdas. those calls sometimes fail, but the errors are not reported properly up the call stack. I would like to solve this using functional programming, so we’re trying out arrow-kt. This would require mapping these json response to one of two things. either a standard failure (the failure is a standard exception message from Quarkus) or our result object.
278 Views