-
Notifications
You must be signed in to change notification settings - Fork 62
Stable release & move to Retrofit repo #58
Comments
That's right, yes. I don't recall any feedback or issues off-hand. We're forced to go through the Java APIs due to Retrofit using a When the format types became stable (for use, not inheritance) we added If you are going to stabilize the API as-is we can move this converter to the Retrofit repo and do an |
We've been revisiting the design, and while the function is likely to stay, I'd like to ask about the problems with nullability: Because Java's type tokens do not contain this information, interface MyApi {
@Get("foo")
fun getFoo(): List<String?> // or any other nullable type in any position
} Would fail with deserialization error on the input like We currently have several proposals on what to do with it:
|
It has not been a problem that I have heard of (or seen myself) because again I think people tend to write concrete types for their request and response objects rather than use a parameterized type. The more complex types, such as those which may be a list of potentially-nullable values, occur within the request or response types where the serialization compiler plugin has full access to the metadata to generate the serializer. Even in the case where it may come up in the future, I think we can tell people to use the surrogate pattern from your docs. |
Thanks for the clarifications. I'll just leave everything as is then and prepare In case this problem reappears later, we can always introduce an additional function as in option 3. |
Upstream stabilization PR: Kotlin/kotlinx.serialization#2069 |
UPD: PR is merged, the only changes are documentation; we plan to release 1.5.0 somewhere after Kotlin 1.8.0 |
Thanks! I'm going to release a stable version of this library for simplicity, and then will migrate it to Retrofit for its next release. |
I want to report that we just ran into this issue. All network responses from our servers are wrapped in an object containing status of the response ( Previously, we used to have dozens of DTOs where the wrapper object was effectively duplicated everywhere, e.g. data class AuthenticateChallengeResponse(
val returnCode: String,
val returnMessage: String,
val errorDetailMap: Map<String, Any>?,
val info: Info?,
) {
data class Info(
val sessionId: String
)
} Instead of duplicating this fixed structure again and again, we've created a generic wrapper: data class ResponseEnvelope<out T>(
val returnCode: String,
val returnMessage: String,
val errorDetailMap: Map<String, String>?,
val info: T,
) Now we can scrap the wrapper: data class AuthenticateChallenge(
val sessionId: String,
) and replace We've ran into issues with nullable types as generic subtypes when using Moshi (square/moshi#1218) and decided to give Kotlin Serialization a try. Tests looked promising - no issues with nullability, But today I've noticed that Retrofit basically has the same issue as Moshi - it does not take into account call-site nullability. @POST("/v1/auth/challenge")
suspend fun authenticateChallenge(
@Body request: AuthenticateChallengeRequest,
): ResponseEnvelope<AuthenticateChallenge?> This fails if the response does not contain the {
"returnCode":"123456",
"returnMessage":"Invalid xxx."
} would throw
This is not on par with how the Kotlin Serialization library works without Retrofit. The following test passes @Test
fun `Kotlin serialization honors call-site nullability`() {
val string = """{
"returnCode":"123456",
"returnMessage":"Invalid xxx."
}""".trimIndent()
val json = Json { explicitNulls = false }
assertThat(
json.decodeFromString<ResponseEnvelope<AuthenticateChallenge?>>(string)
).isEqualTo(
ResponseEnvelope<AuthenticateChallenge?>(
returnCode = "123456",
returnMessage = "Invalid xxx.",
errorDetailMap = null,
info = null,
)
)
} |
In this specific case, it looks like you can solve the problem by changing There's little reason to propagate the requirement of nullability into the type parameter for a wrapper object whose property which uses that type parameter is always nullable. By making the type parameter have a non-null lower bound and changing the property declaration site to always be nullable you make the type simpler to write, you force the property to always be nullable, and you work around our limitation of using Java reflection to denote Kotlin types (without kotlin-reflect). |
I wanted to avoid changing
From Kotlin's null-safety point of view, for the While trying to blame Retrofit for this issue, I've realized that the structure of our responses is at fault too. We basically have a polymorphic response - we mix success and failure in the same object with no way to differentiate the type during deserialization and we perform it manually only after the response is fully parsed. I start to have a feeling that even if this issue was addressed, we would still struggle in some cases. I'll just proceed with your suggestion and use |
If you take your test case above and use it with You could alternatively model it with a sealed type which avoids needing sealed interface ResponseEnvelope<out T : Any>
value class ResponseSuccess<out T : Any>(val value: T) : ResponseEnvelope<T>
data class ResponseFailure(code .., details .., map ..) : ResponseEnvelope<Nothing> |
been waiting the stable release on this one @JakeWharton |
Any updates on this? |
No |
We've released an 1.5.0-RC with stable |
I saw that, thanks! I'll get to this eventually. |
1.0.0 has been released! It'll probably be a few months before it shows up in the Retrofit repo as a proper first-party offering. |
Upstream PR square/retrofit#3950 |
The move is done, just not the release. I am closing this issue since its work is done. A Retrofit release will happen by the end of the year. I will leave this project unarchived until then. After the Retrofit release, I expect to archive this project soon thereafter. Thanks all! |
Hi! As far as I understood from #43 the last thing that prevents this lib from being an official converter in the Retrofit repo is that it depends on the experimental parts of kotlinx.serialization API.
We've removed experimentality from StringFormat/BinaryFormat in 1.3.0 release. Am I right that
serializer(java.lang.Type)
is the only experimental function blocking this?If so, can you please provide some feedback on it — whether it does the right thing, or some functionality is missing, etc. We're going to stabilize it soon so converters like this can depend on a stable API.
The text was updated successfully, but these errors were encountered: