Kotlin / kotlinx.serialization

Kotlin multiplatform / multi-format serialization
Apache License 2.0
5.39k stars 620 forks source link

encodeToString not working when using a data class with default value #1154

Closed AAkira closed 4 years ago

AAkira commented 4 years ago

encodeToString doesn't work when a data class has a default value. Default values are not included in JSON strings.

It works on 1.0.0-RC. It doesn't work since we need to add the "org.jetbrains.kotlinx:kotlinx-serialization-json".

To Reproduce

@Serializable
data class Project(
    val name: String,
    val language: String = "Kotlin",
)

val dataSerialization = Project("kotlinx.serialization", "Kotlin")
val dataCoroutines = Project("kotlinx.coroutines")

val stringSerialization = Json.encodeToString(dataSerialization)
val stringCoroutines = Json.encodeToString(dataCoroutines)

println(stringSerialization) // {"name":"kotlinx.serialization"}
println(stringCoroutines) // {"name":"kotlinx.serialization"}
println(dataCoroutines.language) // Kotlin

Expected behavior

println(stringSerialization) // {"name":"kotlinx.serialization", "language":"Kotlin"}
println(stringCoroutines) // {"name":"kotlinx.serialization", "language":"Kotlin"}

Environment

AAkira commented 4 years ago

I'm sorry. I understood the changelog.

val stringSerialization = Json { encodeDefaults = true }.encodeToString(dataSerialization)
println(stringSerialization) // {"name":"kotlinx.serialization", "language":"Kotlin"}

This code works.

But I think it is complicated to set it up every time. Do you recommend reusing an instance of Json in App?

val json = Json { encodeDefaults = true }
val stringSerialization = json.encodeToString(dataSerialization)
val stringCoroutines = json.encodeToString(dataCoroutines)
elizarov commented 4 years ago

Yes. Please, see the corresponding section of the guide: https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/json.md#json-configuration

It is recommended to store and reuse custom instances of formats for performance reasons as format implementations may cache format-specific additional information about the classes they serialize.

v4dkou commented 3 years ago

@elizarov What is the rationale behind this behaviour being a default in the first place?

This pull request explains it by "reducing visual clutter and saving amount of data being serialized". https://github.com/Kotlin/kotlinx.serialization/pull/1084

But given serialization is often used for transmission of the data across systems (not necessarily developed with the same class definitions in mind and as such possibly having different default values), it's a lossy way to save the amount of data.

It can be considered a good optimization technique when used in projects where it's agreed upon by devs of all systems, but by no means it's a sensible default.

qwwdfsad commented 3 years ago

@v4dkou the rationale is pretty simple: we've found that far more users expect this feature to be "enabled" by default (to be more precise, a lot of users expect serialization to work this way and don't even know about encodeDefaults) rather than disabled. Defaults were previously encoded by default and even Spring maintainers suggested us to reconsider this decision.

Alas, like with any non-trivial feature, there always will be use-cases and/or people who expect the opposite. Unfortunately, we cannot satisfy both parties here

v4dkou commented 3 years ago

@qwwdfsad Got it, thanks for a quick answer!

I suppose people working on microservices (i.e. Spring users) have a different preference, precisely because they only have to consider their inner APIs. Just to counter the balance, here's a bunch of client-side use cases where not serializing default values trips up devs unfamiliar with this decision:

  1. Json.encodeToDynamic -> if JS interop is supposed to be as smooth as it is with JVM one day, something needs to be done about accessing Kotlin objects. Made my head scratch as to why am I losing values on that boundary.
  2. Json.encodeToString -> if I want to send a value to an HTTP service even if no data was given by the user, I expect default values in data classes to express that.
  3. Json.encodeToString -> same goes for when I am writing to a file. I.e. if I'd like to create package.json from a Kotlin-based tool, I'd expect the default "version": "0.0.1" to be written.
diegomarzo commented 1 year ago

I found some behaviour pretty unexpected, if a value has been assigned to the class and by luck is the same to the default value, then the value is not serialised unless you use the encodeDefaults, this is pretty wild.

I could understand the rationale (i don't share it) but this was quite wild.

Just leaving this comment for reference to the people that is using this.