akuleshov7 / ktoml

Kotlin Multiplatform parser and compile-time serializer/deserializer for TOML format (Native, JS, JVM) based on KxS

Home Page:https://akuleshov7.github.io/ktoml

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Exception when decoding map

jakubgwozdz opened this issue · comments

Having this code:

@Serializable
private data class TestData(
    val text: String,
    val map: Map<String, String>,
    val number: Int,
)
fun main() {
    val data = TestData(text = "text value", number = 7321, map = mapOf("a" to "b"))
    val encoded = Toml.encodeToString(data)
    println("=====\n$encoded\n=====")
    val decoded: TestData = Toml.decodeFromString(encoded) // throws MissingRequiredPropertyException
}

The output is this:

=====
text = "text value"
number = 7321

[map]
    a = "b"
=====
Exception in thread "main" 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.LinkedHashMap> in the input. (In your deserialization class you have declared this field, but it is missing in the input)
	at com.akuleshov7.ktoml.decoders.TomlMainDecoder.checkMissingRequiredProperties(TomlMainDecoder.kt:192)
	at com.akuleshov7.ktoml.decoders.TomlMainDecoder.iterateOverStructure(TomlMainDecoder.kt:243)
	at com.akuleshov7.ktoml.decoders.TomlMainDecoder.beginStructure(TomlMainDecoder.kt:212)

Encoding works perfectly well and toml content is generated without problems, but decoding this content back - well, we're not there yet :)

will be investigated this month

thank you for your report and sorry for a long delay!

Still having the issue, is there any workaround? Really need to deserialize libs.versions.toml

@y9san9 sorry for a stupid question, what is the datastucture in toml reflects Maps? Cannot find a simple example.

Any string that you are trying to decode?

Your class Toml produces from:

Toml.encodeToString(mapOf("library" to "1.0.0", "kotlin" to "2.0.0")) 

The following:

library = "1.0.0"
kotlin = "2.0.0"

And I want to decode that back, but cannot do that at the moment. All Toml spec is about to be converted to hash tables (probably that's something similar to hash maps)

Hm, but it's a little bit weird actually:

library = "1.0.0"
kotlin = "2.0.0"

is not a Map... It's a number of properties in Toml specification.

To store it to the map, I will need to guess the type somehow and understand that it is really a Map, but not a property, that user forgot to provide (due to his manual bug).

So how could I deserialize that?

And also that:

coreKtx = { module = "androidx.core:core-ktx", version.ref = "coreKtx" }

All the solutions I found did that using Map

(I don't know the exact names, I want to deserialize arbitrary amount of such rows)

So how could I deserialize that?

And also that:

coreKtx = { module = "androidx.core:core-ktx", version.ref = "coreKtx" }

All the solutions I found did that using Map

hm, let me look into the spec of TOML

So how could I deserialize that?

And also that:

coreKtx = { module = "androidx.core:core-ktx", version.ref = "coreKtx" }

All the solutions I found did that using Map

According to the spec, it's Inline Table https://toml.io/en/v1.0.0#inline-table and we should be able to decode it 🤔

A list of inline tables? If so, that doesn't work either.

@Serializable
data class Libraries(
    val libraries: List<@TomlInlineTable Library>
) {
    @Serializable
    data class Library(val module: String, val version: String)
}

How do I get coreKtx with such setup?

The intuitive approach: to add pair name -> inline table I used the Map type, which is also being encoded in such structure I needed, but is not being decoded back

A list of inline tables?

I think that this might help:
https://github.com/akuleshov7/ktoml#how-ktoml-works-examples

We actually had a test and an example for such structures:
gradle-libs-like-property = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }

your class:
    @SerialName("gradle-libs-like-property")
    val kotlinJvm: GradlePlugin

@Serializable
data class GradlePlugin(val id: String, val version: Version)

But actually I do not like the behaviour when we create a map on serialization and then have ambiguity on the deserialisation with the same class. I understood the problem, but do Inline Tables will help you now in your particular case?

Sorry, as I mentioned earlier that I don't know what names of the libraries will be. I want to parse them and only after that I could tell the names.

In Json when you don't know class layout you would also use Map<String, JsonElement>

Sorry, as I mentioned earlier that I don't know what names of the libraries will be. I want to parse them and only after that I could tell the names.

Got it, so you need to parse properties without explicit naming, and you need to decode it to some map of strings. I do not have an idea of how to support it now, as it is violating TOML spec, but I will think...

Why is it violating TOML specs? It's how Version Catalogs work :)

Why is it violating TOML specs? It's how Version Catalogs work :)

Yes, I mean that the idea of TOML is that you know the names of the properties. That's why this structure:

a = 1

is a NAMED key-value property https://toml.io/en/v1.0.0#keyvalue-pair

It will be a different story to decode the following code:

a = 1
b = 2

to a Map("a" to "1", "b" to "2"), because the original idea is about named keys with typed values:

val a: Int
val b: Int

BUT! Ktoml itself has multiple extensions to the original idea of TOML, so probably we can think of something we can do here. Especially when we encode these values to a Map. Need to think...

Initial implementation is done for simple map: #246 (phase 1)
@jakubgwozdz @y9san9

Phase 2 with nested tables: WIP

Nested Maps (nested tables) done in #252

Released in 0.5.1