3

I'm having a problem with MoshiConverterFactory and Retrofit - I can't send POST JSON request to RestAPI, because it always results in an error. I could do it successfully using Multipart, but the API doesn't support it, so it's out of the question...

My singleton object that holds the reference to the service:

object Singleton {
    val okhttp = OkHttpClient.Builder()
        .addInterceptor(HttpLoggingInterceptor().apply {
            level = HttpLoggingInterceptor.Level.BODY
        })
        .build()

    val moshi = Moshi.Builder().build()

    val retrofit = Retrofit.Builder()
        .baseUrl("https://some_shows_api.com/api/")
        .client(okhttp)
        .addConverterFactory(MoshiConverterFactory.create())
        .build()

    val service = retrofit.create(ShowsApiService::class.java)
}

Register user method that causes the problem in my ViewModel:

    fun registerUser(email: String, password: String) {
        val authData = AuthData(email, password)
        Singleton.service.register(authData).enqueue(object : Callback<Register> {
            override fun onFailure(call: Call<Register>, t: Throwable) {
                // some errror implementation
            }

            override fun onResponse(call: Call<Register>, response: Response<Register>) {
                if (response.isSuccessful) {
                    val body = response.body()
                    if (body != null) {
                        // login user
                    } else {
                        // some error implementation
                    }
                }
            }
        })
    }

My interface:

data class AuthData(
    val email: String,
    val password: String
)

interface ShowsApiService {
    @Headers("Content-Type: application/json")
    @POST("users")
    fun register(@Body authData: AuthData): Call<Register>
}

What I already tried to do: adding @field:Json() annotations on parameters of my AuthData class (error is still the same), passing data with multipart and form-url-encoded (the API doesn't support it). Does anyone see what am I doing wrong? I also don't see some online examples of people using Moshi with Retrofit... Any help or tip will be much appreciated. Thanks!

Error:

FATAL EXCEPTION: main
    Process: com.frideriklab.android_course, PID: 7774
    java.lang.IllegalArgumentException: Unable to create @Body converter for class com.frideriklab.android_course.AuthData (parameter #1)
        for method ShowsApiService.login
        at retrofit2.Utils.methodError(Utils.java:52)
        at retrofit2.Utils.parameterError(Utils.java:57)
        at retrofit2.RequestFactory$Builder.parseParameterAnnotation(RequestFactory.java:735)
        at retrofit2.RequestFactory$Builder.parseParameter(RequestFactory.java:306)
        at retrofit2.RequestFactory$Builder.build(RequestFactory.java:193)
        at retrofit2.RequestFactory.parseAnnotations(RequestFactory.java:67)
        at retrofit2.ServiceMethod.parseAnnotations(ServiceMethod.java:26)
        at retrofit2.Retrofit.loadServiceMethod(Retrofit.java:170)
        at retrofit2.Retrofit$1.invoke(Retrofit.java:149)
        at java.lang.reflect.Proxy.invoke(Proxy.java:1006)
        at $Proxy0.login(Unknown Source)
        at com.frideriklab.android_course.ShowsViewModel.loginUser(ShowsViewModel.kt:85)
        at com.frideriklab.android_course.AuthFragment$onViewCreated$3.onClick(AuthFragment.kt:56)
        at android.view.View.performClick(View.java:6669)
        at android.view.View.performClickInternal(View.java:6638)
        at android.view.View.access$3100(View.java:789)
        at android.view.View$PerformClick.run(View.java:26145)
        at android.os.Handler.handleCallback(Handler.java:873)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:193)
        at android.app.ActivityThread.main(ActivityThread.java:6898)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:537)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
     Caused by: java.lang.IllegalArgumentException: Cannot serialize Kotlin type com.frideriklab.android_course.AuthData. Reflective serialization of Kotlin classes without using kotlin-reflect has undefined and unexpected behavior. Please use KotlinJsonAdapter from the moshi-kotlin artifact or use code gen from the moshi-kotlin-codegen artifact.
        at com.squareup.moshi.ClassJsonAdapter$1.create(ClassJsonAdapter.java:83)
        at com.squareup.moshi.Moshi.adapter(Moshi.java:138)
        at com.squareup.moshi.Moshi.adapter(Moshi.java:98)
        at retrofit2.converter.moshi.MoshiConverterFactory.requestBodyConverter(MoshiConverterFactory.java:106)
        at retrofit2.Retrofit.nextRequestBodyConverter(Retrofit.java:282)
        at retrofit2.Retrofit.requestBodyConverter(Retrofit.java:262)
        at retrofit2.RequestFactory$Builder.parseParameterAnnotation(RequestFactory.java:732)
friderik
  • 117
  • 2
  • 12

2 Answers2

1

For someone looking for answers on why data classes are throwing this error. This could also happen if data class is not getting serialized. You could add serialization like below.

import com.google.gson.annotations.SerializedName
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass

@JsonClass(generateAdapter = true)
data class MyClassName(
    @Json(name = "paramOne")
    @SerializedName("paramOne")
    val paramOne: String,

    @Json(name = "paramTwo")
    @SerializedName("paramTwo")
    val paramTwo: Int
)
Prakash
  • 7,794
  • 4
  • 48
  • 44
0

I also never used Moshi. But you can try this as alternative.

@POST("users")
fun register(@Body authData: Map<String, String>): Call<Register>

And create map as below:

fun registerUser(email: String, password: String) {
    val authData = HashMap<String, String>()
    authData.put("email", email)
    authData.put("password", password)

    Singleton.service.register(authData).

    ...
}
Md. Asaduzzaman
  • 14,963
  • 2
  • 34
  • 46