2

I practicing on Retrofit with Moshi, in this simple demo app I trying to get list of albums and log it, but I got this exception after running the app

E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.anushka.retrofitdemo, PID: 12428
    com.squareup.moshi.JsonDataException: Expected BEGIN_OBJECT but was BEGIN_ARRAY at path $
        at com.squareup.moshi.JsonUtf8Reader.beginObject(JsonUtf8Reader.java:172)
        at com.squareup.moshi.kotlin.reflect.KotlinJsonAdapter.fromJson(KotlinJsonAdapter.kt:67)
        at com.squareup.moshi.internal.NullSafeJsonAdapter.fromJson(NullSafeJsonAdapter.java:41)
        at retrofit2.converter.moshi.MoshiResponseBodyConverter.convert(MoshiResponseBodyConverter.java:46)
        at retrofit2.converter.moshi.MoshiResponseBodyConverter.convert(MoshiResponseBodyConverter.java:27)
        at retrofit2.OkHttpCall.parseResponse(OkHttpCall.java:243)
        at retrofit2.OkHttpCall$1.onResponse(OkHttpCall.java:153)
        at okhttp3.internal.connection.RealCall$AsyncCall.run(RealCall.kt:504)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at java.lang.Thread.run(Thread.java:923)

Data classes

AlbumsItem

@JsonClass(generateAdapter = true)
data class AlbumsItem(
    @Json(name = "id")
    val id: Int,
    @Json(name = "title")
    val title: String,
    @Json(name = "userId")
    val userId: Int
)

Albums List class

@JsonClass(generateAdapter = false)
class Albums : ArrayList<AlbumsItem>()

the service

interface AlbumsService {

    @GET("albums")
    suspend fun getAlbums(): Response<Albums>
}

The instance of retrofit

class RetrofitInstance {
    companion object {
        private const val BASE_URL = "https://jsonplaceholder.typicode.com/"

        var moshi = Moshi.Builder()
            .add(KotlinJsonAdapterFactory())
            .build()


        fun getRetrofitInstance(): Retrofit {

            return Retrofit.Builder()
                .baseUrl(BASE_URL)
                .addConverterFactory(MoshiConverterFactory.create(moshi))
                .build()

        }
    }
}

and finally the MainActivity

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val retService = RetrofitInstance.getRetrofitInstance().create(AlbumsService::class.java)

        val responseLiveData: LiveData<Response<Albums>> = liveData {
            val response:Response<Albums> = retService.getAlbums()
            emit(response)
        }

        responseLiveData.observe(this, {
            val albumsList = it.body()?.listIterator()
            if (albumsList != null) {
                while (albumsList.hasNext()) {
                  albumsList.forEach {
                      Log.d(TAG, "album data by title: ${it.title}")
                  }
                }
            }
        })
    }
}

and while I searching for the cause and solution of this error, I see this question and it look the Moshi cannot recognize the data class Albums and I don't know why, but after I changed the structure of service instead using Albums class to List it's work fine

I replaced the service like this

@GET("albums")
suspend fun getAlbums(): Response<List<AlbumsItem>>

and call it like this

val responseLiveData: LiveData<Response<List<AlbumsItem>>> = liveData {
            val response:Response<List<AlbumsItem>> = retService.getAlbums()
            emit(response)
        }

PS: I tried this annotation @JsonClass(generateAdapter = true) and with value false above the Albums class but it doesn't work, also I tried using GsonConverterFactory with Albums class it's recognize it without problem, so How can I make the moshi recognize it?

Dr Mido
  • 2,414
  • 4
  • 32
  • 72

1 Answers1

0

Maybe for someone it will works. In my case I changed

This:

return Retrofit.Builder().addConverterFactory(MoshiConverterFactory.create(MyMoshiObject).asLenient())                                    
    .baseUrl(Constants.BASE_URL).                              
    .client(okHttpClient)                                                  
    .build().

To this:

return Retrofit.Builder()
.addConverterFactory(MoshiConverterFactory.create().asLenient())
            .baseUrl(Constants.BASE_URL).                              
            .client(okHttpClient)                                                  
            .build()

Just removed Moshi object in create()