Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Values serialized as collections via custom serializers aren't decoded correctly #213

Open
YarnSphere opened this issue Jun 8, 2024 · 2 comments

Comments

@YarnSphere
Copy link

YarnSphere commented Jun 8, 2024

Description

Consider the following toy example:

@Serializable
data class User(val name: String, val shoppingCart: ShoppingCart)

@Serializable(with = ShoppingCartSerializer::class)
data class ShoppingCart(val items: MutableList<Item>) {
    val total: Double get() = items.sumOf { it.price }
}

@Serializable
data class Item(val name: String, val price: Double)

Where ShoppingCartSerializer is a custom serializer, which serializes ShoppingCart as a list:

class ShoppingCartSerializer : KSerializer<ShoppingCart> {
    private val listSerializer = ListSerializer(Item.serializer())

    override val descriptor: SerialDescriptor =
        SerialDescriptor("org.example.ShoppingCart", listSerializer.descriptor)

    override fun serialize(encoder: Encoder, value: ShoppingCart): Unit =
        encoder.encodeSerializableValue(listSerializer, value.items)

    override fun deserialize(decoder: Decoder): ShoppingCart =
        ShoppingCart(decoder.decodeSerializableValue(listSerializer).toMutableList())
}

The following test, which should pass, does not:

val data = User(
    "Alice",
    ShoppingCart(mutableListOf(Item("T-Shirt", 20.0), Item("Boots", 50.0)))
)

@Test
fun testCustomCollectionXmlSerialization() {
    val encodedXml = XML.encodeToString(data)
    val decodedXml = XML.decodeFromString<User>(encodedXml)

    assertEquals(
        """<User name="Alice"><Item name="T-Shirt" price="20.0"/><Item name="Boots" price="50.0"/></User>""",
        encodedXml
    )
    assertEquals(data, decodedXml)
}

The first assert passes, i.e., encoding is correct.
The second assert, however, fails with:

Expected :User(name=Alice, shoppingCart=ShoppingCart(items=[Item(name=T-Shirt, price=20.0), Item(name=Boots, price=50.0)]))
Actual   :User(name=Alice, shoppingCart=ShoppingCart(items=[Item(name=Boots, price=50.0)]))

I.e., decoding drops all items of the collection but the last.

Notes

  • Using a standard "collection" (List, Set, Array, etc.) for the shopping cart directly instead of a class with a delegated serializer results in the decoding working as expected.
  • Changing the custom serializer's implementation to delegate to SetSerializer or any other "built-in" collection serializer results in the same incorrect behaviour.
  • The custom serializer's deserialize function is called once for each item of the collection, instead of only being called once for the whole collection.
  • The kotlinx.serialization JSON serializer has no problems with this example.

Reproduction

The code above showcasing the issue is available at: https://github.com/YarnSphere/xmlutil-custom-collection-serialization

Versions tested

  • 0.86.3 with Kotlin 1.9.24
  • 0.90.0-RC2 with Kotiln 2.0.0
@YarnSphere YarnSphere changed the title Values serialized as a collection via a custom serializer aren't decoded correctly Values serialized as collections via custom serializers aren't decoded correctly Jun 8, 2024
@pdvrieze
Copy link
Owner

pdvrieze commented Jun 8, 2024

Unfortunately this cannot work currently as list deserialization explicitly uses the AbstractCollectionSerializer to handle incremental reading of list elements. To avoid that the deserializer would need to buffer all list entries and only provide them at the end, as list.

@YarnSphere
Copy link
Author

I see, that is indeed unfortunate. Appreciate the prompt reply!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants