orchestr7 / ktoml

Kotlin Multiplatform parser and compile-time serializer/deserializer for TOML format (Native, JS, JVM) based on KxS
https://akuleshov7.github.io/ktoml
MIT License
455 stars 25 forks source link

encodeToString produces wrong indentation for case when there are nested List among the tree #254

Open vlsi opened 10 months ago

vlsi commented 10 months ago
class NestedListsTest {
    @Serializable
    data class Wrapper(
        val elements: List<Element>
    )

    @Serializable
    data class Element(
        val name: String,
        val subElements: List<SubElement>
    )

    @Serializable
    data class SubElement(
        val name: String,
        val description: String,
    )

    @Test
    fun test() {
        val data = Wrapper(
            elements = listOf(
                Element(
                    name = "element 1",
                    subElements = listOf(
                        SubElement("1.1", "d1.1"),
                        SubElement("1.2", "d1.2"),
                    )
                ),
            )
        )
        val serialized = Toml.encodeToString(data)

        Assert.assertEquals(
            serialized,
            """
            [[elements]]
                name = "element 1"

                [[elements.subElements]]
                    name = "1.1"
                    description = "d1.1"

                [[elements.subElements]]
                    name = "1.2"
                    description = "d1.2"
            """.trimIndent()
        )

        val deserialized = Toml.decodeFromString<Wrapper>(serialized)

        Assert.assertEquals(data, deserialized)
    }
}

com.akuleshov7:ktoml-core:0.5.1 yields

[[elements]]
    name = "element 1"

    [[elements.subElements]]
        name = "1.1"
        description = "d1.1"

[[elements.subElements]]
        name = "1.2"
        description = "d1.2"

The generated TOML does not parse. It would be nice if the error message included the problematic field name or something like that. Currently the error message provides little to no clue on why the parsing went wrong:

com.akuleshov7.ktoml.exceptions.MissingRequiredPropertyException: Invalid number of key-value arguments provided in the input for deserialization. Missing required property <0> from class <kotlin.collections.ArrayList> in the input. (In your deserialization class you have declared this field, but it is missing in the input)
    at app//com.akuleshov7.ktoml.decoders.TomlMainDecoder.checkMissingRequiredProperties(TomlMainDecoder.kt:200)
    at app//com.akuleshov7.ktoml.decoders.TomlMainDecoder.iterateOverTomlStructure(TomlMainDecoder.kt:258)
    at app//com.akuleshov7.ktoml.decoders.TomlMainDecoder.beginStructure(TomlMainDecoder.kt:220)
    at app//kotlinx.serialization.internal.AbstractCollectionSerializer.merge(CollectionSerializers.kt:29)
    at app//kotlinx.serialization.internal.AbstractCollectionSerializer.deserialize(CollectionSerializers.kt:43)
    at app//kotlinx.serialization.encoding.Decoder$DefaultImpls.decodeSerializableValue(Decoding.kt:257)
    at app//kotlinx.serialization.encoding.AbstractDecoder.decodeSerializableValue(AbstractDecoder.kt:16)
    at app//com.akuleshov7.ktoml.decoders.TomlAbstractDecoder.decodeSerializableValue(TomlAbstractDecoder.kt:96)
    at app//kotlinx.serialization.encoding.AbstractDecoder.decodeSerializableValue(AbstractDecoder.kt:43)
    at app//kotlinx.serialization.encoding.AbstractDecoder.decodeSerializableElement(AbstractDecoder.kt:70)
    at app//com.example.NestedListsTest$Wrapper$$serializer.deserialize(TomlTest.kt:11)
    at app//com.example.NestedListsTest$Wrapper$$serializer.deserialize(TomlTest.kt:11)
    at app//kotlinx.serialization.encoding.Decoder$DefaultImpls.decodeSerializableValue(Decoding.kt:257)
    at app//kotlinx.serialization.encoding.AbstractDecoder.decodeSerializableValue(AbstractDecoder.kt:16)
    at app//com.akuleshov7.ktoml.decoders.TomlAbstractDecoder.decodeSerializableValue(TomlAbstractDecoder.kt:96)
    at app//com.akuleshov7.ktoml.decoders.TomlMainDecoder$Companion.decode(TomlMainDecoder.kt:293)
    at app//com.akuleshov7.ktoml.Toml.decodeFromString(Toml.kt:47)
    at app//com.example.NestedListsTest.test(TomlTest.kt:66)
vlsi commented 10 months ago

In case you wonder,

    @Serializable
    data class Wrapper(
        val elements: List<Element>
    )

    @Serializable
    data class Element(
        val name: String,
        val subElements: List<SubElement>
    )

    @Serializable
    data class SubElement(
        val name: String,
        val description: String,
        val subSubElements: List<SubSubElement>,
    )

    @Serializable
    data class SubSubElement(
        val name: String,
        val description: String,
    )

with

val data = Wrapper(
    elements = listOf(
        Element(
            name = "element 1",
            subElements = listOf(
                SubElement(
                    "1.1", "d1.1",
                    subSubElements =
                    listOf(
                        SubSubElement("1.1.1", "d1.1.1"),
                        SubSubElement("1.1.2", "d1.1.2")
                    )
                ),
                SubElement(
                    "1.2", "d1.2",
                    subSubElements =
                    listOf(
                        SubSubElement("1.2.1", "d1.2.1"),
                        SubSubElement("1.2.2", "d1.2.2"),
                    )
                ),
            )
        ),
        Element(
            name = "element 2",
            subElements = listOf(
                SubElement(
                    "2.1", "d2.1",
                    subSubElements =
                    listOf(
                        SubSubElement("2.1.1", "d2.1.1"),
                        SubSubElement("2.1.2", "d2.1.2"),
                    )
                ),
                SubElement(
                    "2.2", "d2.2",
                    subSubElements =
                    listOf(
                        SubSubElement("2.2.1", "d2.2.1"),
                        SubSubElement("2.2.2", "d2.2.2"),
                    )
                ),
            )
        ),
    )
)

yields

[[elements]]
    name = "element 1"

    [[elements.subElements]]
        name = "1.1"
        description = "d1.1"

        [[elements.subElements.subSubElements]]
            name = "1.1.1"
            description = "d1.1.1"

[[elements.subElements.subSubElements]]
            name = "1.1.2"
            description = "d1.1.2"

[[elements.subElements]]
        name = "1.2"
        description = "d1.2"

        [[elements.subElements.subSubElements]]
            name = "1.2.1"
            description = "d1.2.1"

[[elements.subElements.subSubElements]]
            name = "1.2.2"
            description = "d1.2.2"

[[elements]]
    name = "element 2"

    [[elements.subElements]]
        name = "2.1"
        description = "d2.1"

        [[elements.subElements.subSubElements]]
            name = "2.1.1"
            description = "d2.1.1"

[[elements.subElements.subSubElements]]
            name = "2.1.2"
            description = "d2.1.2"

[[elements.subElements]]
        name = "2.2"
        description = "d2.2"

        [[elements.subElements.subSubElements]]
            name = "2.2.1"
            description = "d2.2.1"

[[elements.subElements.subSubElements]]
            name = "2.2.2"
            description = "d2.2.2"
orchestr7 commented 9 months ago

Decoding fails due to ArrayOfTables https://toml.io/en/v1.0.0#array-of-tables . It has mostly a zero support in our lib.

But spacing should be fixed, definitely