What does kotlin serialization do with `{"a": "a",...
# serialization
d
What does kotlin serialization do with
{"a": "a", "b":true, "c":1, "d": 10000000000000000000}
(if I want it as
Map<String, Any>
), and would it differentiate between
Int
Long
Float
and
Double
? Or does it just put all numbers in one of those (In Moshi, they just put them all in Doubles... https://github.com/square/moshi/issues/1144)? Do I need to manually serialize all this or is there an easy way to do this?
d
Any
just isn't supported like that out of the box. You'll have to write a small custom serializer to parse a
JsonPrimitive
and pull out the value from it.
d
The docs are a bit big, and I'm new to kotlinx serialization... can you point me to the section I would need? Thanks!
j
I highly recommend using control/cmd F in their serialization guide index, I find always everything I need and there are a lot of samples
d
I guess that's for someone who's worked with it before... But you also can't do ctrl-f between chapters...
I'm used to Moshi, and wanted to get a bit of a start on kotlinx serialization, but I don't really have time to go through all those docs properly for this. I have a deadline for a current project... Truth is, if I need to do it manually, I could always use Moshi's JsonReader... I was just curious if Kotlinx serialization could do this better...
Thanks anyways 😉! I guess I'll need to read up on things one of these days!
d
Unfortunately, it looks like the type can't be read from the primitive.
Copy code
class SpecialAnySerializer : KSerializer<Any> {
	override val descriptor: SerialDescriptor get() = JsonPrimitive.serializer().descriptor

	override fun deserialize(decoder: Decoder): Any {
		val primitive = decoder.decodeSerializableValue(JsonPrimitive.serializer())
		return primitive.long
	}

	override fun serialize(encoder: Encoder, value: Any) {
		TODO("Not yet implemented")
	}
}
d
😮 So it's not even possible?
d
So you'll have to try each type from most strict to least strict and check if it throws.
Why do you need
Any
?
You could do
Map<String, JsonPrimitive>
.
d
Well, I need to parse the claim in a JWT Token, and they can be string, long, int or boolean (those are the types Keycloak can return in those tokens...). The goal is to take those parsed claims and store them in an Android
SharedPreferences
file each one as it's original type...
The
JsonPrimitive
would make the
Repository
implementation dependant on the
Parser
implementation... to convert it to
Map<String, Any>
to pass to the repository is an extra step... but I guess that would be one possibility...
d
Here you go.
Copy code
class SpecialAnySerializer : KSerializer<Any> {
	override val descriptor: SerialDescriptor get() = JsonPrimitive.serializer().descriptor

	override fun deserialize(decoder: Decoder): Any {
		val primitive = decoder.decodeSerializableValue(JsonPrimitive.serializer())
		return primitive.longOrNull ?: primitive.doubleOrNull ?: primitive.booleanOrNull ?: primitive.content
	}

	override fun serialize(encoder: Encoder, value: Any) {
		// encoder.encodeSerializableValue(JsonPrimitive.serializer(), JsonPrimitive(value))
	}
}
d
That looks great! Thanks, I'll give it a try!
Just a little thing @Dominaezzz, how do I use it? I tried:
Copy code
Json.decodeFromString<Map<String, Any>>(SpecialAnySerializer(), """
    {
        "a": "a",
        "b": 1111,
        "c": 11111111111111111,
        "d": 111111111111111111111111111111111111111111111111111
    }
""".trimIndent())
but it looks like that's wrong...
I don't see any way to tell kotlinx serialization to apply it only to the values of the map...
d
Copy code
Json.decodeFromString<Map<String, Any>>(MapSerializer(String.serializer(), SpecialAnySerializer()), """
    {
        "a": "a",
        "b": 1111,
        "c": 11111111111111111,
        "d": 111111111111111111111111111111111111111111111111111
    }
""".trimIndent())
Typically you'd do
Map<String, @Serializable(SpecialAnySerializer::class) Any>
within a class.
d
Oooh, thanks! That did it! I didn't know you could apply that annotation to a type parameter...
But even the first way looks pretty nice if I'd save the serializer in a seperate
val
inside the parser class.