-
Notifications
You must be signed in to change notification settings - Fork 761
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
Undocumented behavior on serialization of non-constructor property with defaults #1803
Comments
Here's a short test case to display the issue (it uses assertK for the assertion):
|
Note that these versions did not support Kotlin and would serialize backing fields as well as not honor default argument values on deserialization. Behavior on Kotlin types was completely unspecified. |
Encode defaults does not control whether constant instance properties are serialized or not. It controls the behavior when a property value matches that of its default. |
Hi Jake, first of all thank you for the super quick answer.
It actually does the former when encoding a model to a string. This test passes using the same model:
Aside from the behaviour of another library, which can choose to take another approach on subtleties like this one, I'm curious to know if there was a rationale behind Moshi's choice. To me, aside from the "working by accident" in older versions, it seems like a counterintuitive behaviour that can easily lead to bugs that are harder to spot. Something that's even more obscure to me is the fact (which I just noticed) that if you turn the
This outputs the following with Moshi:
Whether this is or not an intuitive behaviour, can you or anybody else think of a "global" workaround via adapters? Thank you again 🙇 |
That is not what is happening. The non-primary constructor property is always considered as a serializable member by kotlinx.serialization in your example. What the Moshi does not consider a |
I understand what you meant now, thanks for the explanation. So the goal of Moshi in terms of serialization/deserialization is being symmetric at all costs? My use case (which I assume is a very common one) is using Moshi for serialization of request models and deserialization of response models through OkHttp/Retrofit. In this context, the symmetric behaviour isn't important, at least compared to predictability and ease of use. The specific given example was based on a common scenario, in our codebase, where there's a constant to be sent to an endpoint (e.g. an application code). I don't want to give the ability to client code to modify this constant, but I also don't want to have the need to specify that constant every time I instantiate the class. And I do need to use a data class for logging purposes. In order to retain these constraints and get this to work with Moshi, I'd have to write this:
Which is a lot more verbose and hard to read than the previous approach, let alone the fact the
|
Suggestion: since this is a limitation due to a technical constraint in |
There is no technical limitation. It is a design choice that was made when Kotlin support was added. |
I might have misunderstood then. From what you wrote in your previous comment, not serialising I understand that, but if it weren't for that limitation, why else would the library not serialise a property that is neither transient nor marked as ignored? Without any warning as well? |
It has nothing to do with Kotlin reflection. You cannot write to a non-primary constructor |
Okay, fair enough, a I still don't see how it's consistent behaviour though, aside from the symmetricity rationale, from the point of view of an all-purpose serialisation library. IMHO in this instance "failing silently" (= not serialising the property) isn't a very predictable behaviour for the developer declaring it. From a serialisation standpoint, it shouldn't make a difference whether it's a val or a var, if the developer doesn't declare they want to ignore it, ignoring it silently is dangerous and error-prone. I think it would be extremely useful to have a way to override this behaviour in the frequent scenarios (request/response mapping) where the symmetric approach isn't needed (as opposed to caching or storing data, where I acknowledge is fundamental). Please let me know if it's worth creating a separate issue as a feature request. Thank you! |
Moshi is definitely not all-purpose. It has been designed very intentionally to not support any and everything that one would want to do. However, we try to cover the most common ~90% and offer escape hatches when you need to do something more advanced. kotlinx.serialization is the same way, but it may choose different values to include and exclude in the features it supports.
I think calling this "frequent" is a bit of an exaggeration as no one else has been clamoring for this in the six years of Kotlin support. Today, you can achieve this with a custom For the long term, we can have a discussion about whether this is something we want to allow. But I would keep in mind that no one is really working on Moshi actively so I would not rely on waiting for this feature if you need it today. |
Using
moshi-kotlin:1.15.1
withKotlinJsonAdapterFactory
:I want to serialize the following model:
I would expect the class JSON serialization to yield the following output:
(and this, in fact, was what happened until Moshi <= 1.8)
But I get the following on the latest Moshi:
The property outside the primary constructor is ignored. I would consider this an undocumented behavior and I honestly struggle to understand why this is happening. I would expect the property to be serialized since it's not marked as
@Transient
or@Json(ignored=true)
. Note that this also happens with normal (nondata
) classes.The
kotlin-serialization
library allows to configure this aspect of encoding with the optionencodeDefaults=true
. I couldn't find anything equivalent in Moshi.I have a legacy codebase with 300+ request/response models, and making one-by-one changes to the classes would be both unfeasible and extremely error-prone. Is there a way around this?
Thank you in advance.
The text was updated successfully, but these errors were encountered: