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?