pdvrieze / xmlutil

XML Serialization library for Kotlin
https://pdvrieze.github.io/xmlutil/
Apache License 2.0
379 stars 30 forks source link

Deserialization issues with 0.90.2+ #254

Open hrkfdn opened 1 day ago

hrkfdn commented 1 day ago

This problem occurs with 0.90.2 and 0.90.3. See the following example:

class Test {
    @Serializable
    @SerialName("element")
    data class Element(
        @XmlElement(true)
        val a: String
    )

    @Serializable
    @SerialName("element")
    data class OtherElement(
        @XmlElement(true)
        val b: String
    )

    @Serializable
    data class Parent(
        val element: Element,
    )

    @Serializable
    data class OtherParent(
        val element: OtherElement,
    )

    @Serializable
    data class Root(
        val parent: Parent,
        val otherParent: OtherParent,
    )

    @Test
    fun `xml version 0_9_x test`() {
        val xml = XML {
            indent = 4
        }

        val root = Root(
            parent = Parent(Element("element")),
            otherParent = OtherParent(OtherElement("element")),
        )
        val serialized = xml.encodeToString(root)
        println(serialized)
        val deserialized: Root = XML.decodeFromString(serialized)

        assertEquals(root, deserialized)
    }
}

This will print the following XML:

<Root>
    <Parent>
        <element>
            <a>element</a>
        </element>
    </Parent>
    <OtherParent>
        <element>
            <a>element</a>
        </element>
    </OtherParent>
</Root>

Note how in OtherParent/element the tag is <a>, even though it should be <b>.

pdvrieze commented 21 hours ago

I've confirmed the issue. It is a caching issue. To get it to work you can disable the cache using (or with whatever policy configuration you use):

XML {
  indent = 4
  defaultPolicy {
      formatCache = FormatCache.Dummy
  }
}
pdvrieze commented 20 hours ago

I've analysed it. And the key reason is that Element and OtherElement have the same serial name, and as such their types can not be distinguished (and their descriptors are effectively identical). If you instead use @XmlSerialName it will work correctly as that annotation is not transparent. Note that this is a fundamental limitation of the serialization library. Looking at the implementation of PluginGeneratedSerialDescriptor (and the debugger) the serialDescriptors of both are equal (despite the element names being different).

pdvrieze commented 20 hours ago

This needs "fixing" in the serialization library itself. In the meantime consider that having two types with equal @SerialName annotation in the same serialization context is not supported.

hrkfdn commented 20 hours ago

Makes sense, thanks for investigating!