https://kotlinlang.org logo
#serialization
Title
# serialization
s

Stylianos Gakis

03/06/2024, 4:41 PM
I modeled a class which I want to serialize as a value class just so that it’s a bit safer to use while not showing up nested in the resulting Json, something like this
Copy code
@Serializable
data class Outer(val inner: Inner)

@Serializable
@JvmInline
public value class Inner(public val url: String)
I then realized that value classes do not get exported to our iOS target properly, so I am thinking I’d rather just make it not be a value class anymore. However this then has implications on serialization. For an object like
Outer(Inner("someValue"))
Before, with
value
keyword:
Copy code
{
  "inner": "someValue"
}
After, without the
value
keyword:
Copy code
{
  "inner": {
    "url": "someValue"
  }
}
Is there a way to still keep the old behavior while not having my class be a value class and therefore not degrade the experience of the iOS target?
e

ephemient

03/06/2024, 10:13 PM
with a custom serializer for this one class,
Copy code
@Serializable(with = InnerSerializer::class)
class Inner(val url: String)

object InnerSerializer : KSerializer<Inner> {
    override val descriptor: SerialDescriptor = SerialDescriptor("Inner", String.serializer().descriptor)
    override fun serialize(encoder: Encoder, value: Inner) = encoder.encodeString(value.url)
    override fun deserialize(decoder: Decoder): Inner = Inner(decoder.decodeString())
}
do you need it just for this, or do you want something more generic?
if so,
Copy code
class InlineSerializer<Container, Value>(
    serialName: String,
    private val serializer: KSerializer<Value>,
    private val unwrap: (Container) -> Value,
    private val wrap: (Value) -> Container,
) : KSerializer<Container> {
    override val descriptor: SerialDescriptor = SerialDescriptor(serialName, serializer.descriptor)
    override fun serialize(encoder: Encoder, value: Container) = encoder.encodeSerializableValue(serializer, unwrap(value))
    override fun deserialize(decoder: Decoder): Container = wrap(decoder.decodeSerializableValue(serializer))
}

object InnerSerializer : KSerializer<Inner> by InlineSerializer("Inner", String.serializer(), Inner::url, ::Inner)
and the latter can be repeated for as many types as you need
s

Stylianos Gakis

03/07/2024, 8:00 AM
Wow yes didn't think of that! Thanks a lot for the code too, I think I can use this. Really appreciate your help!
2 Views