ktoml icon indicating copy to clipboard operation
ktoml copied to clipboard

How to deserialise Nested-Tables?

Open Kinchkun opened this issue 1 year ago • 1 comments

Cheers,

thank you for this library :)

Assuming I have the following case:

@Serializable
data class Animals(
    val animals: Map<String, Animal>
)

@Serializable
data class Animal(
    val name: String,
    val vocal: String
)

@Test
fun `can parse animals`() {
    // language=toml
    val tomlString = """
    [animals."my cat"]
    name = "maunz"
    vocal = "miau"

    [animals."my dog"]
    name = "bello"
    vocal = "wuff"
    """.trimIndent()
    val animals = Toml.decodeFromString<Animals>(tomlString)
}

I got the following error:

com.akuleshov7.ktoml.exceptions.IllegalTypeException: Line 1: 
            You are trying to decode a nested Table animals."my cat" with a <Map> type to some primitive type.
            For example: 
            [a]
              [a.b]
                  a = 2
                  
            should be decoded to Map<String, Map<String, Long>>, but not to Map<String, Long>

Firstly, I don't think this error message is correct. "Animal" is not a primitive type, or?

However, when I follow the suggestions and change animals: Map<String, Animal> to animals: Map<String, Map<String, Animal>> I didn't get an error, but the types are wrong:

image

As you can see I got Map<String, Map<String,String>>.

The other way however works like charm:

@Test
fun `can serialise animals`() {
    val animals = Animals(mapOf(
        "my cat" to Animal("maunz", "miau"),
        "my dog" to Animal("bello", "wuff"),
    ))

    val tomlString = Toml.encodeToString(animals)
    println(tomlString) 
/* Produces
[animals."my cat"]
    name = "maunz"
    vocal = "miau"

[animals."my dog"]
    name = "bello"
    vocal = "wuff"
*/
}

Do I misunderstand something?

Kind regards

Kinchkun avatar Jan 16 '24 16:01 Kinchkun

Hi! Thank you for your very good question ❤️

Actually by the initial implementation of ktoml we expected to have strict naming schema of the decoded type (where you was not able to use Map, but had to explicitly name your fields like “my dog” and “my cat”):

val `my dog`: Animal

this will work in your case if you know the names of nested tables.

Later due to a number of requests, we decided to add anonymous fields and decode them to a Map. And looks like I forgot to support a corner case: when you don’t know the names of nested tables, but know their type.

It’s a bug, need to fix it…

In your case there is a quick workaround with nested map: Map<String, Map<String, String> - that should work. Or you may name nested tables explicitly as I pointed above with my dog example.

orchestr7 avatar Jan 19 '24 06:01 orchestr7