Recently I have been working on project which includes working with API. I have checked it in Postman and I am sure that its response is an Array of Objects but when tried to use it I faced Expected BEGIN_OBJECT but was BEGIN_ARRAY error so I changed the API response type to List to see what happens and I realized instead of Array of objects it returns Arrays of object's arrays and in all of them objects are null and have no valid response.
fun createApiService(): ApiService {
val client = OkHttpClient.Builder()
.addInterceptor(
BasicAuthInterceptor(
"username",
"password"
)
)
.build()
val api = Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build()
return api.create(ApiService::class.java)
}
class BasicAuthInterceptor(username: String, password: String) : Interceptor {
private var credentials: String = Credentials.basic(username, password)
override fun intercept(chain: Interceptor.Chain): okhttp3.Response {
var request = chain.request()
request = request.newBuilder().header("Authorization", credentials).build()
return chain.proceed(request)
}
}
This part is for creating API and giving it username and password because it is private and it needs Authorization:
data class ProductResponse(
val products: List<Product>
)
data class Product(
/// its attributes are here
)
interface ApiService {
@GET("products")
suspend fun getAllProducts(): List<ProductResponse>
}
class ProductRepositoryImpl(
private val apiService: ApiService,
) : ProductRepository {
override suspend fun getProducts(isInternetConnected: Boolean): List<Product> {
val dataFromServer = apiService.getAllProducts()
Log.v("api",dataFromServer.toString())
return dataFromServer[0].products
}
}
and Log shows:
[ProductResponse(products=null), ProductResponse(products=null), ProductResponse(products=null), ProductResponse(products=null), ProductResponse(products=null), ProductResponse(products=null), ProductResponse(products=null), ProductResponse(products=null), ProductResponse(products=null), ProductResponse(products=null)]
[{
"name": "test",
"type": "simple",
"featured": false,
"catalog_visibility": "visible",
"description": "",
"short_description": "",
"price": "351000",
"regular_price": "351000",
"sale_price": "",
"on_sale": false,
"purchasable": true,
"total_sales": 0,
"button_text": "",
"tax_status": "taxable",
"tax_class": "",
"manage_stock": false,
"stock_quantity": null,
"backorders": "no",
"backorders_allowed": false,
"backordered": false,
"low_stock_amount": null,
"sold_individually": false,
"weight": "",
"shipping_required": true,
"shipping_taxable": true,
"shipping_class": "",
"shipping_class_id": 0,
"reviews_allowed": true,
"average_rating": "0.00",
"rating_count": 0,
"parent_id": 0,
"purchase_note": "",
and it continues like this and is more than 600 lines
The return type of your ApiService
methods should directly be the desired response type (or it can be wrapped inside a retrofit2.Response
, e.g. Response<MyClass>
). There is no need for the separate ProductResponse
class you have, which just holds the actual response data.
So since your API returns a JSON array, your service method should have List<...>
as return type (or any other type which Gson can deserialize from a JSON array).
The reason why you are getting a lot of ProductResponse(products=null)
is that you have specified List<ProductResponse>
as return type. So Gson expects the following JSON structure:
[
{
"products": [
{ ... },
...
]
},
{
"products": [
{ ... },
...
]
},
...
]
(and because Gson ignores missing fields, ProductResponse#products
simply remains null
)
To solve this, change the return type of getAllProducts
and delete the ProductResponse
class:
@GET("products")
suspend fun getAllProducts(): List<Product>