xmlutil icon indicating copy to clipboard operation
xmlutil copied to clipboard

Problem with maps

Open joseluisgs opened this issue 3 years ago • 3 comments

Hi Fist of all, thank your work, I love. I have a problem serialization a map, from Kotlin to XML. I don't know if I am wrong or is a problem. I have this class

@Serializable
@SerialName("estadistica")
data class Estadistica(
    @Serializable(with = UUIDSerializer::class)
    val id: UUID = UUID.randomUUID(),
    @XmlElement(true)
    val resumenes: Map<String, Resumen> = mapOf(),
    @Serializable(with = LocalDateTimeSerializer::class)
    val createdAt: LocalDateTime = LocalDateTime.now(),
)

I have a problem with the map. If I change map for list, I don´t have any problem, but with map I have the exception

this is my code

val informeXml = myScope.launch {
            logger.debug { "Guardando informe en XML..." }
            val informe = InformeDto(
                estadisticas = listOf(
                    EstadisticaDto(
                        resumenes = mapOf(
                            "a" to ResumenDto(tipo = "NO2"),
                            "b" to ResumenDto(tipo = "CO"),
                            "c" to ResumenDto(tipo = "Ozone"),
                        )
                    ),
                )
            )
    val xml = XML {
        indentString = "  "
        autoPolymorphic = true
    }
    xmlFile.writeText(xml.encodeToString(informe))
}
informeXml.join()

No problem if change map for list

The exception is:

Exception in thread "DefaultDispatcher-worker-5" java.lang.IndexOutOfBoundsException: Index 2 out of bounds for length 2
	at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:64)
	at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:70)
	at java.base/jdk.internal.util.Preconditions.checkIndex(Preconditions.java:266)
	at java.base/java.util.Objects.checkIndex(Objects.java:359)
	at java.base/java.util.ArrayList.get(ArrayList.java:427)
	at nl.adaptivity.xmlutil.serialization.structure.XmlCompositeDescriptor.getElementDescriptor(XmlDescriptor.kt:670)
	at nl.adaptivity.xmlutil.serialization.XmlEncoderBase$TagEncoder.encodeSerializableElement(XMLEncoder.kt:396)
	at kotlinx.serialization.internal.MapLikeSerializer.serialize(CollectionSerializers.kt:122)
	at nl.adaptivity.xmlutil.serialization.XmlEncoderBase$TagEncoder$encodeSerializableElement$3.invoke(XMLEncoder.kt:424)
	at nl.adaptivity.xmlutil.serialization.XmlEncoderBase$TagEncoder$encodeSerializableElement$3.invoke(XMLEncoder.kt:424)
	at nl.adaptivity.xmlutil.serialization.XmlEncoderBase$TagEncoder.endStructure(XMLEncoder.kt:568)
	at dto.EstadisticaDto$$serializer.serialize(EstadisticaDto.kt:11)
	at dto.EstadisticaDto$$serializer.serialize(EstadisticaDto.kt:11)
	at nl.adaptivity.xmlutil.serialization.XmlEncoderBase$ListEncoder.encodeSerializableElement$xmlutil_serialization(XMLEncoder.kt:940)
	at nl.adaptivity.xmlutil.serialization.XmlEncoderBase$TagEncoder.encodeSerializableElement(XMLEncoder.kt:396)
	at kotlinx.serialization.internal.CollectionLikeSerializer.serialize(CollectionSerializers.kt:69)
	at nl.adaptivity.xmlutil.serialization.XmlEncoderBase$TagEncoder$encodeSerializableElement$3.invoke(XMLEncoder.kt:424)
	at nl.adaptivity.xmlutil.serialization.XmlEncoderBase$TagEncoder$encodeSerializableElement$3.invoke(XMLEncoder.kt:424)
	at nl.adaptivity.xmlutil.serialization.XmlEncoderBase$TagEncoder.endStructure(XMLEncoder.kt:568)
	at dto.InformeDto$$serializer.serialize(InformeDto.kt:17)
	at dto.InformeDto$$serializer.serialize(InformeDto.kt:17)
	at nl.adaptivity.xmlutil.serialization.XML.encodeToWriter(XML.kt:259)
	at nl.adaptivity.xmlutil.serialization.XML.encodeToWriter(XML.kt:205)
	at nl.adaptivity.xmlutil.serialization.XML.encodeToString(XML.kt:149)
	at nl.adaptivity.xmlutil.serialization.XML.encodeToString(XML.kt:136)
	at dto.InformeDtoKt.writeToXmlFile(InformeDto.kt:41)
	at controllers.R2D2Controller$saveData$2$informeXml$1.invokeSuspend(R2D2Controller.kt:163)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
	at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
	at kotlinx.coroutines.internal.LimitedDispatcher.run(LimitedDispatcher.kt:42)
	at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:95)
	at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:570)
	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:677)
	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:664)
	Suppressed: kotlinx.coroutines.DiagnosticCoroutineContextException: [StandaloneCoroutine{Cancelling}@2b0cb275, Dispatchers.IO]
07:31:27.587 [DefaultDispatcher-worker-5] DEBUG controllers.R2D2Controller - Datos salvados exitosamente
Tiempo de procesamiento: 320ms

Process finished with exit code 0

Could you help me? Thanks!

joseluisgs avatar Oct 04 '22 05:10 joseluisgs

I've had a preliminary look here. While there is clearly a bug with the descriptors (that I've fixed in dev), there is a broader problem in that maps basically aren't supported, and are not that trivial to support nicely. They are serialized with interleaved keys and values and that is just not correct in XML. I'm working on solving that but doing that in a way that I would know is robust is a bit tricky so it will take a bit. In the meantime, there may be some mileage in serializing maps as lists of "entries".

pdvrieze avatar Oct 06 '22 20:10 pdvrieze

Oh thank you again. Yes a List of entries was my solution. Thank you again

joseluisgs avatar Oct 07 '22 05:10 joseluisgs

I've implemented it on the development branch. It should also be available soon as snapshot (this is the only difference with the release). I haven't tested yet with polymorphic keys or values although it should work. It is also configurable using a custom/overriding policy, but there are as yet no easy-configuration annotations or such implemented. The default implementation uses the list elision setting, and if the key can be written as attribute (ie. it is like a primitive) and that property name is not present in the value it will write it as a tag with the value, but the key additional.

There is one limitation in that map serialization requires keys to be read before values. In the case that the key is a tag (and there thus is a wrapping entry), this could be out of order. This is not yet supported by the format (it requires reading the value content into memory, storing it and then retrieving the key. Handling of unexpected tag content is not done well yet either.

pdvrieze avatar Oct 14 '22 19:10 pdvrieze

Thank you so much! i don't try it. I will wait to stable version. But I know it will be a fantastic feature :)

joseluisgs avatar Oct 16 '22 11:10 joseluisgs

Now released in 0.85.0.

pdvrieze avatar Feb 19 '23 15:02 pdvrieze