question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Allow generic types in responses when using Moshi as JSON parser

See original GitHub issue

What kind of issue is this?

  • Question. This issue tracker is not the place for questions. If you want to ask how to do something, or to understand why something isn’t working the way you expect it to, use Stack Overflow. https://stackoverflow.com/questions/tagged/retrofit

  • Bug report. If you’ve found a bug, spend the time to write a failing test. Bugs with tests get fixed. Here’s an example: https://gist.github.com/swankjesse/6608b4713ad80988cdc9

  • Feature Request. Start by telling us what problem you’re trying to solve. Often a solution already exists! Don’t send pull requests to implement new features without first getting our support. Sometimes we leave features out on purpose to keep the project small.

Description of the issue:

I’m trying to achieve a scenario that is very common, but with Moshi isn’t so trivial as I thought (I’m coming from Gson, that is simpler to use, but I’m aware of its many disadvantages).

I have endpoint definitions that use generic types, like this:

    @GET("api.json")
    suspend fun <T> test(): Result<T>

This is used because Result<T> contains a standard structure, basically as defined below:

@JsonClass(generateAdapter = true)
data class Result<T> (

    var data: T? = null,

    var timestamp: OffsetDateTime? = null
)

As you may see, it is used to track common information (like the server timestamp and, eventually, in the future, may have the ID of the request to match logs or something like that).

But the problem is that Moshi adapter for this class isn’t called how it should by Retrofit (I explain below), which results in this error:

2020-01-18 15:38:59.913 7097-7202/com.example.myapplication E/MainActivity: Error handling JSON data: 
    java.lang.IllegalArgumentException: Unable to create converter for com.example.myapplication.api.Result<T>
        for method RemoteService.test
        at retrofit2.Utils.methodError(Utils.java:53)
        at retrofit2.HttpServiceMethod.createResponseConverter(HttpServiceMethod.java:115)
        at retrofit2.HttpServiceMethod.parseAnnotations(HttpServiceMethod.java:82)
        at retrofit2.ServiceMethod.parseAnnotations(ServiceMethod.java:37)
        at retrofit2.Retrofit.loadServiceMethod(Retrofit.java:192)
        at retrofit2.Retrofit$1.invoke(Retrofit.java:149)
        at java.lang.reflect.Proxy.invoke(Proxy.java:813)
        at $Proxy4.test(Unknown Source)
        at com.example.myapplication.MainActivity.handleWithMoshiRetrofitConverter(MainActivity.kt:72)
        at com.example.myapplication.MainActivity$onCreate$1$1.invokeSuspend(MainActivity.kt:48)
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
        at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:56)
        at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:561)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:727)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:667)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:655)
     Caused by: java.lang.IllegalArgumentException: No JsonAdapter for T (with no annotations)
    for T data
    for com.example.myapplication.api.Result<T>
        at com.squareup.moshi.Moshi$LookupChain.exceptionWithLookupStack(Moshi.java:349)
        at com.squareup.moshi.Moshi.adapter(Moshi.java:150)
        at com.example.myapplication.api.ResultJsonAdapter.<init>(ResultJsonAdapter.kt:29)
        at java.lang.reflect.Constructor.newInstance0(Native Method)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:430)
        at com.squareup.moshi.internal.Util.generatedAdapter(Util.java:540)
        at com.squareup.moshi.StandardJsonAdapters$1.create(StandardJsonAdapters.java:60)
        at com.squareup.moshi.Moshi.adapter(Moshi.java:138)
        at com.squareup.moshi.Moshi.adapter(Moshi.java:98)
        at retrofit2.converter.moshi.MoshiConverterFactory.responseBodyConverter(MoshiConverterFactory.java:91)
        at retrofit2.Retrofit.nextResponseBodyConverter(Retrofit.java:352)
        at retrofit2.Retrofit.responseBodyConverter(Retrofit.java:335)
        at retrofit2.HttpServiceMethod.createResponseConverter(HttpServiceMethod.java:113)
        	... 14 more
     Caused by: java.lang.IllegalArgumentException: No JsonAdapter for T (with no annotations)
        at com.squareup.moshi.Moshi.adapter(Moshi.java:148)
        	... 25 more

Firstly, I thought the problem was with Moshi generated adapter. But then I made a simple test code that uses Moshi directly to parse the text and then try the same with a Retrofit call. The Moshi part of handling it is a little bit confusing (maybe the generated adapter code could be improved for this kind of scenarios), but it works very well.

So I ended up thinking that this may be the way Retrofit call Moshi converter. Any thoughts or ideas?

PS: I know I could create a response model for each of my response possibilities, but I really don’t think that this would be a solution. I don’t see, at least right now, why Retrofit + Moshi couldn’t handle this kind of use case (except for the fact that it isn’t implemented yet, which seems to be the case).

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Comments:9 (1 by maintainers)

github_iconTop GitHub Comments

5reactions
luanmmcommented, Feb 11, 2020

Thanks for the suggestion, @20ali20.

I ended up, for now, using Gson as parser. I read all over that it isn’t a good library for many reasons, but it is more flexible than Moshi in some use cases.

Will try to replace Gson at some point and your suggestion will be taken into account!

I will close the issue, as it is answered and there is no point to keep it open (I think).

Thanks for the attention of you all.

1reaction
20ali20commented, Feb 11, 2020

Hello, @JakeWharton. Thanks for your soon reply!

There is no way to implement this, then? If there is no way to figure out T to call Moshi (or any other library), I can’t see a way to achieve it from Retrofit helpers, indeed.

  1. Any suggestions on how to achieve something like that without using generics in Retrofit definitions?
  2. Maybe Retrofit could warn somehow, in compilation time, that this is not a valid interface construction to map a remote service (if possible and not difficult to implement).

Thanks for the attention!

Have you tried creating an instance of the generic class with the specific T type in mind and then passing the class of that ? This way you have a new class where T is defined.

Also another option is to replace T with Any/Object and convert it after the matter

Read more comments on GitHub >

github_iconTop Results From Across the Web

How To Deserialize Generic Types with Moshi? - Stack Overflow
With Gson, I can create a JsonDeserializer<Vehicle> that can examine the __typename field, identify whether this is a Car or Boat , then...
Read more >
Advanced JSON parsing techniques using Moshi and Kotlin
The most basic way of parsing JSON using Moshi is to use the streaming API, ... primitive types ( Long , Int )...
Read more >
Solving the Moshi enumeration problem using generics
Moshi is one of the most popular JSON parsing libraries on Android that allows us to convert a JSON object into a data...
Read more >
How to use generic types with retrofit and moshi in kotlin?
[Solved]-How to use generic types with retrofit and moshi in kotlin?-kotlin ... Allow generic types in responses when using Moshi as JSON parser...
Read more >
Retrieving reified generic arguments - hrach.dev
But when the function is inlined and the generic parameter is reified , you can work with it as an actual type. Moshi,...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found