Search code examples
androidjsonkotlinretrofitget-request

Fetch content from API with Retrofit and Kotlin


I have trouble fetching data from my API while it works perfectly with Insomnia

JSON from my API

{
  "count": 4,
  "products": [
    {
      "name": "Tapas",
      "price": 10,
      "productImage": "uploads\\2021-04-04T15-43-36.826Z_tapas.png",
      "_id": "6069dea870f35f0f90242f4a",
      "request": {
        "type": "GET",
        "url": "https://katobackend.azurewebsites.net/api/product/6069dea870f35f0f90242f4a"
      }
    },
    {
      "name": "Cupcake",
      "price": 6,
      "productImage": "uploads\\2021-04-04T20-04-27.036Z_detail-5.jpg",
      "_id": "606a1bcba294652bb03e6952",
      "request": {
        "type": "GET",
        "url": "https://katobackend.azurewebsites.net/api/product/606a1bcba294652bb03e6952"
      }
    },
    {
      "name": "Bottle",
      "price": 6,
      "productImage": "uploads/2021-05-23T14-27-33.336Z_Water.jpg",
      "_id": "60aa6655898ea9002454b27f",
      "request": {
        "type": "GET",
        "url": "https://katobackend.azurewebsites.net/api/product/60aa6655898ea9002454b27f"
      }
    },
    {
      "name": "Bottle",
      "price": 6,
      "productImage": "uploads/2021-05-23T14-36-50.590Z_Water.jpg",
      "_id": "60aa688245340b0024735b5c",
      "request": {
        "type": "GET",
        "url": "https://katobackend.azurewebsites.net/api/product/60aa688245340b0024735b5c"
      }
    }
  ]
}

Product Response:

data class ProductResponse (
    @SerializedName("count")
    var count: Int,

    @SerializedName("products")
    var products: List<Product>
)

Product :

data class Product (
    @SerializedName("_id")
    var _id: Int,

    @SerializedName("name")
    var name: String,

    @SerializedName("price")
    var price: Number,

    @SerializedName("productImage")
    var productImage: String
)

UserClient:

interface ApiService {

    @GET(Constants.PRODUCTS_URL)
    fun fetchPosts(@Header("auth-token") token: String): Call<ProductResponse>

}

API Client:

class ApiClient {
    private lateinit var apiService: ApiService

    fun getApiService(): ApiService {

        // Initialize ApiService if not initialized yet
        if (!::apiService.isInitialized) {
            val retrofit = Retrofit.Builder()
                .baseUrl(Constants.BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .build()

            apiService = retrofit.create(ApiService::class.java)
        }

        return apiService
    }

}

Authenticated Activity:

class Authenticated : AppCompatActivity() {

    private lateinit var sessionManager: SessionManager
    private lateinit var apiClient: ApiClient

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

        lukaku.setOnClickListener(){
            fetchPosts()
        }
    }

    private fun fetchPosts() {

        apiClient = ApiClient()
        sessionManager = SessionManager(this)

        // Pass the token as parameter
        //println("${sessionManager.fetchAuthToken()}")

        apiClient.getApiService().fetchPosts(token = "${sessionManager.fetchAuthToken()}")
            .enqueue(object : Callback<ProductResponse> {
                override fun onFailure(call: Call<ProductResponse>, t: Throwable) {
                    Log.e("", t.message, t)
                }

                override fun onResponse(call: Call<ProductResponse>, response: Response<ProductResponse>) {
                    Log.d("Response", response.toString())
                    println(response)
                    println(response.body())
                }
            })
    }
}

I'm using an auth-token as header and I can get the token from my Activity without any problem but can't get products even link for API is working

Logcat:

06-01 10:08:05.111 6739-6783/com.mlp.project E/Surface: getSlotFromBufferLocked: unknown buffer: 0x7f4a5805aa00
    com.google.gson.JsonSyntaxException: java.lang.NumberFormatException: Invalid double: "6069dea870f35f0f90242f4a"
        at com.google.gson.internal.bind.TypeAdapters$7.read(TypeAdapters.java:228)
        at com.google.gson.internal.bind.TypeAdapters$7.read(TypeAdapters.java:218)
        at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.read(ReflectiveTypeAdapterFactory.java:131)
        at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:222)
        at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.read(TypeAdapterRuntimeTypeWrapper.java:41)
        at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:82)
        at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:61)
        at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.read(ReflectiveTypeAdapterFactory.java:131)
        at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:222)
        at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:40)
        at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:27)
        at retrofit2.OkHttpCall.parseResponse(OkHttpCall.java:243)
        at retrofit2.OkHttpCall$1.onResponse(OkHttpCall.java:153)
        at okhttp3.RealCall$AsyncCall.run(RealCall.kt:138)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
        at java.lang.Thread.run(Thread.java:818)
     Caused by: java.lang.NumberFormatException: Invalid double: "6069dea870f35f0f90242f4a"
        at java.lang.StringToReal.invalidReal(StringToReal.java:63)
        at java.lang.StringToReal.initialParse(StringToReal.java:114)
        at java.lang.StringToReal.parseDouble(StringToReal.java:282)
        at java.lang.Double.parseDouble(Double.java:301)
        at com.google.gson.stream.JsonReader.nextInt(JsonReader.java:1201)
        at com.google.gson.internal.bind.TypeAdapters$7.read(TypeAdapters.java:226)
        at com.google.gson.internal.bind.TypeAdapters$7.read(TypeAdapters.java:218) 
        at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.read(ReflectiveTypeAdapterFactory.java:131) 
        at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:222) 
        at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.read(TypeAdapterRuntimeTypeWrapper.java:41) 
        at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:82) 
        at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:61) 
        at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.read(ReflectiveTypeAdapterFactory.java:131) 
        at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:222) 
        at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:40) 
        at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:27) 
        at retrofit2.OkHttpCall.parseResponse(OkHttpCall.java:243) 
        at retrofit2.OkHttpCall$1.onResponse(OkHttpCall.java:153) 
        at okhttp3.RealCall$AsyncCall.run(RealCall.kt:138) 
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113) 
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588) 
        at java.lang.Thread.run(Thread.java:818) 

Solution

  • Your _id key is coming in as a string value while you have kept it as an Int in the data model, which is causing exception while conversion using Gson, I would suggest you update the data model as following and try again.

    data class Product (
        @SerializedName("_id")
        var _id: String,
    
        @SerializedName("name")
        var name: String,
    
        @SerializedName("price")
        var price: Number,
    
        @SerializedName("productImage")
        var productImage: String
    )