KordK
Kord11mo ago
Tic

(Not Kord) Deserialize JSON with unordered fields (Kotlin Serialization)

Hello

I have this deserialization function from my custom Serializer:
override fun deserialize(decoder: Decoder): PacketWrapper {
        return decoder.decodeStructure(descriptor) {
            var type: Byte? = null
            var data: Packet? = null

            if(decodeSequentially()) {
                type = decodeByteElement(descriptor, 0)
                val serializer = typeToPacket[type] ?: throw SerializationException("The field 'type' is required")
                data = decodeSerializableElement(descriptor, 1, serializer)
                PacketWrapper(data)
            } else {
                loop@ while (true) {
                    when (val index = decodeElementIndex(descriptor)) {
                        0 -> type = decodeByteElement(descriptor, index)
                        1 -> {
                            val serializer = typeToPacket[type] ?: throw SerializationException("The field 'type' is required")
                            data = decodeSerializableElement(descriptor, index, serializer as KSerializer<Packet>)
                        }
                        CompositeDecoder.DECODE_DONE -> break@loop
                        else -> throw SerializationException("Unexpected index: $index")
                    }
                }

                type ?: throw SerializationException("The field 'type' is required")
                data ?: throw SerializationException("The field 'data' is required")
                PacketWrapper(data)
            }
        }
    }

When the JSON is:
{"type":0,"data":{"message":"Hello, World!"}}

That works perfectly.

But when the json is:
{"data":{"message":"Hello, World!"},"type":0}

The exception
The field 'type' is required
(from the loop) is thrown.

is there a way to support unordered fields?
Solution
@Serializable
sealed interface Packet {
  val data: Data
  
  interface Data
}

@Serializable
@SerialName("hello")
data class HelloPacket(override val data: Data) : Packet {
  @Serializable
  data class Data(val pingInterval: Int) : Packet.Data
}
Was this page helpful?