I have tried for the past couple of days to migrate my project from GSON to Moshi. So far so good, up to the point when I prepared a release build.
For reference, I am using Retrofit with Moshi
implementation "com.squareup.retrofit2:converter-moshi:$retrofit_moshi_version"
implementation "com.squareup.moshi:moshi-kotlin:$moshi_version"
Moshi instance
val moshi: Moshi = Moshi.Builder()
.add(DateMoshiAdapter())
.addLast(KotlinJsonAdapterFactory())
.build()
Retrofit instance
private val retrofit: Retrofit by lazy {
Retrofit.Builder()
.addConverterFactory(
MoshiConverterFactory.create(moshi)
)
.baseUrl(baseUrl)
.client(httpClient)
.build()
}
Now, let's consider simple data models as the following ones
#Keep
#Parcelize
data class BodyModel(
#Json(name = "info")
val dhInfo: DhInfoModel
) : Parcelable
#Keep
#Parcelize
data class DhInfoModel(
#Json(name = "type")
val type: String,
#Json(name = "value")
val value: String
) : Parcelable
The error that I get when I want to execute the request (which works fine on debug builds) is the following
Unable to create #Body converter for class package.name.etc.api.data.BodyModel (parameter #1)
What I tried ?
using proguard rules found on different issues, like this one
using addLast to the KotlinJsonAdapterFactory() as it was mentioned here
using #field:Json() annotation for data class fields
using JsonClass(generateAdapter = true)
Later Edit:
All this moshi and retrofit is on the SDK part, which I include into my application after I build the release SDK. What is even more weird, is that I tried adding all of this directly into the application, with the same proguard rules, and it works fine on a sample request.
I am running out of ideas and StackOverflow suggestions. Any informations are appreciated.
Thanks in advance, and happy coding!
Related
I'm making cryptocurrency information viewer now. I use Retrofit to get response, but have some problem.
https://api.coinone.co.kr/ticker/?currency=all
This is the api I use. and its structure is... too complicated to make pojo class.
if I make convert this to pojo class, then pojo class will be like
data class Coinone(
val result: String,
val errorCode: String,
val timestamp: String,
val crypto1: Ticker,
val crypto2: Ticker,
val crypto3: Ticker,
...
val cryptoN: Ticker)
Should I do like that? Is that only one solution?
I mean, if api data would be updated, I must update my pojo class. If I don't, apps will surely make crushes.
Is there any solution for making pojo class with api having similar structure?
Gson Object used when I don't know about Retrofit, GsonConverter and Kotlin not so much.
I'm using Moshi and its CodeGen to generate Kotlin JsonAdaptors automatically for Retrofit's ConvertorFactory.
I add #JsonClass(generateAdapter = true) on top of my data classes and after make project all adapters will be available. Then I define MoshiConverterFactory.create(moshi) in retrofit builder like this:
val moshi = Moshi.Builder().build()
Retrofit.Builder()
.baseUrl(/* baseUrl */)
.client(/* okHttpClient */)
.addConverterFactory(MoshiConverterFactory.create(moshi))
.addCallAdapterFactory(/* ... */)
.build()
My question is: How Moshi will find the generated adapters?
As you can see, there's not required to register them as custom JsonAdapters for Moshi. They work well. (I checked them in debug mode and their function will be invoked)
It's my pleasure if you refer me to its source code.
Moshi uses naming conventions to find the generated adapters. It attempts to find those classes in the expected location, and if they're found then they're used.
I'm using Retrofit to make some API calls.
Recently, I added a new endpoint
#POST("api/test/myRequest")
fun createRequest(
#retrofit2.http.Body request: MyRequest
): Single<MyResponse>
Using the following DataClass to represent the non-serialized request
data class MyRequest(
#Json(name = "myData") #field:Json(name = "myData") var myData: String,
#Json(name = "myuuid") #field:Json(name = "myuuid") var myuuid: UUID? = null
)
When I try to make the request, it fails with the error: java.lang.IllegalStateException: Dangling name: myuuid
The top item in the stacktrace is: com.squareup.moshi.JsonUtf8Writer. It seems Moshi is throwing an error when trying to serialize the request. It's not clear to me why though.
What does this error mean, and how can I fix it?
I would suggest you to use only one of the annotations, for instance you get the issue if you do not use #Json( name, but only #field
I am not sure and could cancel this post if did not help you, but please try, I am quite curious
I am working an Android project that uses RxJava for its network calls and now I'm migrating it to Coroutines. Since the code base is large, I have to use RxJava and Coroutines simultaneously and iteratively convert RxJava methods to Coroutines to eventually lead to a code base that is fully based on Coroutines. Moshi is the converter of choice. When I use RxJava, the network calls work fine and return expected values. But my suspend methods that utilize coroutines are not returning data as expected. The returned POJOs are not populated by the JSON response values.
Here goes my code for building Retrofit
retrofit = Retrofit.Builder()
.baseUrl(BuildConfig.BASE_URL)
.addConverterFactory(MoshiConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.createWithScheduler(Schedulers.io()))
.client(buildHttpClient())
.build()
And here goes my suspend method where the POJO in the body of response is not populated.
#PUT(EndPoints.SOME_ENDPOINT + "{some_id}/")
suspend fun addSomethingSomewhere(#Body body: RequestBody, #Path("some_id") someID: String): Response<SomeModel>
Here are different versions of the POJO I have tried
// version 1
#Parcelize
data class SomeModel(var someValue: String? = null,
var someOtherValue: List<String>? = null)
// version 2
#JsonClass(generateAdapter = true)
class SomeModel(#field:Json(name = "someValue") var someValue: String? = null,
#field:Json(name = "someOtherValue") var someOtherValue: List<String>? = null)
I have tried replacing the Response<SomeModel> with only SomeModel in return type of suspend method, but it still doesn't work. I think the issue has got something to do with this line when building retrofit addCallAdapterFactory(RxJava2CallAdapterFactory.createWithScheduler(Schedulers.io())) because the call adapter is provided for RxJava, and maybe that is why my coroutine-based code is not populating the POJOs as expected. Any help will be appreciated. Thanks!
Recently I switched to MoshiConverterFactory from GSONConverterFactory. Every thing s working fine except the one call. Like other API calls here also I am using #Body annotation but I am getting this error
java.lang.IllegalArgumentException: Unable to create #Body converter for class
my request class :
data class DemoRequest(
val emailId: String? = null,
val demoData: List<DemoDomain?>? = null,
val userName: String? = null
)
One more thing here to mention that with GSONConverterFactory it is working fine but as I switched to MoshiConverterFactory it is throwing error.
retrofitVersion = '2.3.0'
service interface:
#POST("call/api")
fun sendToServer(#Body request: DemoRequest):retrofit2.Call<RemoteResponse>
val retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.client(okHttpClient)
.addConverterFactory(MoshiConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build()
UPDATE-------------
I was sending Date object in request so I need to use custom adapter and it is working fine now
Did you remember to change to MoshiConverterFactory when you build Retrofit?
Retrofit.Builder().baseUrl(...).addConverterFactory(MoshiConverterFactory.create()).build()
Also, the latest version of Retrofit is 2.5.0 so you could try upgrading and make sure your converter is also the same version.