Retrofit android Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path $

461 views Asked by At

I am new to Retrofit and I am having a weird problem. I am getting the following error -->

Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path $

However when I hit the endpoint with a curl command I get back a json array.

EDIT

Sorry for all of the edits. I may have put this question up prematurely. I am considering just removing the unnecessary code snippets if they are distracting. I put the edits at the top so you don't have to scroll down to see the latest problems. I think I may be in some real trouble with this last problem.

I found using a different model structure helps with the json conversion. I switched this

data class Jobs(
    val job: List<JobPW>,
    val status : String
)

To this

data class JobApiModel(
    val jobs: List<Job>,
    val status: String
)

And created a new Job class with the matching elements

data class Job(
    @SerializedName("city")
    @field:Json(name = "city")
    val city: String,
    @SerializedName("job_id")
    @field:Json(name = "job_id")
    val job_id: Int,
    @SerializedName("owner_id_map")
    @field:Json(name = "owner_id_map")
    val owner_id_map: Int,
    @SerializedName("status")
    @field:Json(name = "status")
    val status: Int,
    @SerializedName("title")
    val title: String,
    val workers: List<Worker>,
    val posters: List<PosterWithWorker>
)

This is the same object as JobPW but it is called Job which is the name of the property. Can you use a @SerializedName or something similar for class names? JobPW is a Room relation with a Job object and I can't call both classes Job. What is the best way to handle this?

So both

workers : List<Workers> 

and

List<PosterWithWorker>

deserialize correctly. The other fields are all null and empty. Is there anything I am doing wrong or anything left to do?

EDIT

I am getting closer and it appears that after adding the outer jobs object around the array the call doesn't fail with an exception anymore. Instead, I get a null array (jobs = null) from Retrofit response = Jobs(job=null).

My Room Dao objects are below near the bottom of the post

For some reason, the JSON response looks different when using Retrofit. It looks like the following.

I should mention this is coming from a Firebase Realtime Database. When I use curl I just see the array, there is no wrapper object.

{
  "jobs": [
    {
      "job": {
        "city": "Toronto",
        "job_id": 1,
        "owner_id_map": 1,
        "status": 0,
        "title": "500 Posters in Toronto"
      },
      "posters": [
        {
          "poster": {
            "job_id_map": 2,
            "latitude": 43.64526533333377,
            "locationDescription": "Union Station",
            "longitude": -79.38060468539248,
            "poster_id": 1,
            "qr_code": "code2",
            "worker_id": 1
          },
          "worker": [
            {
              "workerName": "Noris",
              "worker_id": 1
            }
          ]
        },
        {
          "poster": {
            "job_id_map": 2,
            "latitude": 43.64295669900133,
            "locationDescription": "Queen and Spadina",
            "longitude": -79.39368099382459,
            "poster_id": 2,
            "qr_code": "code3",
            "worker_id": 2
          },
          "worker": [
            {
              "workerName": "Jack",
              "worker_id": 2
            }
          ]
        },
        {
          "poster": {
            "job_id_map": 3,
            "latitude": 43.76892443268843,
            "locationDescription": "North York Civic Center",
            "longitude": -79.41292484238735,
            "poster_id": 3,
            "qr_code": "code4",
            "worker_id": 3
          },
          "worker": [
            {
              "workerName": "Nicky",
              "worker_id": 3
            }
          ]
        }
      ],
      "workers": [
        {
          "workerName": "Noris",
          "worker_id": 1
        },
        {
          "workerName": "Jack",
          "worker_id": 2
        },
        {
          "workerName": "Nicky",
          "worker_id": 3
        },
        {
          "workerName": "Davis",
          "worker_id": 4
        },
        {
          "workerName": "Argyle",
          "worker_id": 5
        },
        {
          "workerName": "James",
          "worker_id": 6
        }
      ]
    },
    {
      "job": {
        "city": "Montreal",
        "job_id": 2,
        "owner_id_map": 1,
        "status": 1,
        "title": "20 Posters in Montreal"
      },
      "posters": [],
      "workers": [
        {
          "workerName": "Noris",
          "worker_id": 1
        },
        {
          "workerName": "Jack",
          "worker_id": 2
        },
        {
          "workerName": "Nicky",
          "worker_id": 3
        },
        {
          "workerName": "Davis",
          "worker_id": 4
        },
        {
          "workerName": "Argyle",
          "worker_id": 5
        },
        {
          "workerName": "James",
          "worker_id": 6
        }
      ]
    },
    {
      "job": {
        "city": "Ottawa",
        "job_id": 3,
        "owner_id_map": 2,
        "status": 2,
        "title": "350 Posters in Ottawa"
      },
      "posters": [
        {
          "poster": {
            "job_id_map": 4,
            "latitude": 43.63904362027761,
            "locationDescription": "Harbourfront",
            "longitude": -79.38193214610098,
            "poster_id": 4,
            "qr_code": "code5",
            "worker_id": 0
          },
          "worker": []
        },
        {
          "poster": {
            "job_id_map": 5,
            "latitude": 43.68347235901327,
            "locationDescription": "Bathurst and St Clair",
            "longitude": -79.41851002097324,
            "poster_id": 5,
            "qr_code": "code6",
            "worker_id": 0
          },
          "worker": []
        },
        {
          "poster": {
            "job_id_map": 2,
            "latitude": 43.6958481647278,
            "locationDescription": "Dufferin and Eglinton",
            "longitude": -79.45035794609939,
            "poster_id": 6,
            "qr_code": "code7",
            "worker_id": 0
          },
          "worker": []
        }
      ],
      "workers": [
        {
          "workerName": "Noris",
          "worker_id": 1
        },
        {
          "workerName": "Jack",
          "worker_id": 2
        },
        {
          "workerName": "Nicky",
          "worker_id": 3
        },
        {
          "workerName": "Davis",
          "worker_id": 4
        },
        {
          "workerName": "Argyle",
          "worker_id": 5
        },
        {
          "workerName": "James",
          "worker_id": 6
        }
      ]
    },
    {
      "job": {
        "city": "Calgary",
        "job_id": 4,
        "owner_id_map": 2,
        "status": 0,
        "title": "10 Posters in Calgary"
      },
      "posters": [
        {
          "poster": {
            "job_id_map": 2,
            "latitude": 43.64526533333377,
            "locationDescription": "Union Station",
            "longitude": -79.38060468539248,
            "poster_id": 1,
            "qr_code": "code2",
            "worker_id": 1
          },
          "worker": [
            {
              "workerName": "Noris",
              "worker_id": 1
            }
          ]
        },
        {
          "poster": {
            "job_id_map": 2,
            "latitude": 43.64295669900133,
            "locationDescription": "Queen and Spadina",
            "longitude": -79.39368099382459,
            "poster_id": 2,
            "qr_code": "code3",
            "worker_id": 2
          },
          "worker": [
            {
              "workerName": "Jack",
              "worker_id": 2
            }
          ]
        },
        {
          "poster": {
            "job_id_map": 3,
            "latitude": 43.76892443268843,
            "locationDescription": "North York Civic Center",
            "longitude": -79.41292484238735,
            "poster_id": 3,
            "qr_code": "code4",
            "worker_id": 3
          },
          "worker": [
            {
              "workerName": "Nicky",
              "worker_id": 3
            }
          ]
        }
      ],
      "workers": [
        {
          "workerName": "Noris",
          "worker_id": 1
        },
        {
          "workerName": "Jack",
          "worker_id": 2
        },
        {
          "workerName": "Nicky",
          "worker_id": 3
        },
        {
          "workerName": "Davis",
          "worker_id": 4
        },
        {
          "workerName": "Argyle",
          "worker_id": 5
        },
        {
          "workerName": "James",
          "worker_id": 6
        }
      ]
    }
  ],
  "status": "success"
}

Anyone understand why this maybe happening? Why would the curl response be different?

Here is the json from Firebase using curl.

[
  {
    "job": {
      "city": "Toronto",
      "job_id": 1,
      "owner_id_map": 1,
      "status": 0,
      "title": "500 Posters in Toronto"
    },
    "posters": [
      {
        "poster": {
          "job_id_map": 2,
          "latitude": 43.64526533333377,
          "locationDescription": "Union Station",
          "longitude": -79.38060468539248,
          "poster_id": 1,
          "qr_code": "code2",
          "worker_id": 1
        },
        "worker": [
          {
            "workerName": "Noris",
            "worker_id": 1
          }
        ]
      },
      {
        "poster": {
          "job_id_map": 2,
          "latitude": 43.64295669900133,
          "locationDescription": "Queen and Spadina",
          "longitude": -79.39368099382459,
          "poster_id": 2,
          "qr_code": "code3",
          "worker_id": 2
        },
        "worker": [
          {
            "workerName": "Jack",
            "worker_id": 2
          }
        ]
      },
      {
        "poster": {
          "job_id_map": 3,
          "latitude": 43.76892443268843,
          "locationDescription": "North York Civic Center",
          "longitude": -79.41292484238735,
          "poster_id": 3,
          "qr_code": "code4",
          "worker_id": 3
        },
        "worker": [
          {
            "workerName": "Nicky",
            "worker_id": 3
          }
        ]
      }
    ],
    "workers": [
      {
        "workerName": "Noris",
        "worker_id": 1
      },
      {
        "workerName": "Jack",
        "worker_id": 2
      },
      {
        "workerName": "Nicky",
        "worker_id": 3
      },
      {
        "workerName": "Davis",
        "worker_id": 4
      },
      {
        "workerName": "Argyle",
        "worker_id": 5
      },
      {
        "workerName": "James",
        "worker_id": 6
      }
    ]
  },
  {
    "job": {
      "city": "Montreal",
      "job_id": 2,
      "owner_id_map": 1,
      "status": 1,
      "title": "20 Posters in Montreal"
    },
    "workers": [
      {
        "workerName": "Noris",
        "worker_id": 1
      },
      {
        "workerName": "Jack",
        "worker_id": 2
      },
      {
        "workerName": "Nicky",
        "worker_id": 3
      },
      {
        "workerName": "Davis",
        "worker_id": 4
      },
      {
        "workerName": "Argyle",
        "worker_id": 5
      },
      {
        "workerName": "James",
        "worker_id": 6
      }
    ]
  },
  {
    "job": {
      "city": "Ottawa",
      "job_id": 3,
      "owner_id_map": 2,
      "status": 2,
      "title": "350 Posters in Ottawa"
    },
    "posters": [
      {
        "poster": {
          "job_id_map": 4,
          "latitude": 43.63904362027761,
          "locationDescription": "Harbourfront",
          "longitude": -79.38193214610098,
          "poster_id": 4,
          "qr_code": "code5",
          "worker_id": 0
        }
      },
      {
        "poster": {
          "job_id_map": 5,
          "latitude": 43.68347235901327,
          "locationDescription": "Bathurst and St Clair",
          "longitude": -79.41851002097324,
          "poster_id": 5,
          "qr_code": "code6",
          "worker_id": 0
        }
      },
      {
        "poster": {
          "job_id_map": 2,
          "latitude": 43.6958481647278,
          "locationDescription": "Dufferin and Eglinton",
          "longitude": -79.45035794609939,
          "poster_id": 6,
          "qr_code": "code7",
          "worker_id": 0
        }
      }
    ],
    "workers": [
      {
        "workerName": "Noris",
        "worker_id": 1
      },
      {
        "workerName": "Jack",
        "worker_id": 2
      },
      {
        "workerName": "Nicky",
        "worker_id": 3
      },
      {
        "workerName": "Davis",
        "worker_id": 4
      },
      {
        "workerName": "Argyle",
        "worker_id": 5
      },
      {
        "workerName": "James",
        "worker_id": 6
      }
    ]
  },
  {
    "job": {
      "city": "Calgary",
      "job_id": 4,
      "owner_id_map": 2,
      "status": 0,
      "title": "10 Posters in Calgary"
    },
    "posters": [
      {
        "poster": {
          "job_id_map": 2,
          "latitude": 43.64526533333377,
          "locationDescription": "Union Station",
          "longitude": -79.38060468539248,
          "poster_id": 1,
          "qr_code": "code2",
          "worker_id": 1
        },
        "worker": [
          {
            "workerName": "Noris",
            "worker_id": 1
          }
        ]
      },
      {
        "poster": {
          "job_id_map": 2,
          "latitude": 43.64295669900133,
          "locationDescription": "Queen and Spadina",
          "longitude": -79.39368099382459,
          "poster_id": 2,
          "qr_code": "code3",
          "worker_id": 2
        },
        "worker": [
          {
            "workerName": "Jack",
            "worker_id": 2
          }
        ]
      },
      {
        "poster": {
          "job_id_map": 3,
          "latitude": 43.76892443268843,
          "locationDescription": "North York Civic Center",
          "longitude": -79.41292484238735,
          "poster_id": 3,
          "qr_code": "code4",
          "worker_id": 3
        },
        "worker": [
          {
            "workerName": "Nicky",
            "worker_id": 3
          }
        ]
      }
    ],
    "workers": [
      {
        "workerName": "Noris",
        "worker_id": 1
      },
      {
        "workerName": "Jack",
        "worker_id": 2
      },
      {
        "workerName": "Nicky",
        "worker_id": 3
      },
      {
        "workerName": "Davis",
        "worker_id": 4
      },
      {
        "workerName": "Argyle",
        "worker_id": 5
      },
      {
        "workerName": "James",
        "worker_id": 6
      }
    ]
  }
]

Here is my Retrofit API call

 @GET("/jobs.json")
   suspend fun getJobsList() : Response<List<JobPW>>

I am assuming there is an issue with a nested object but I don't know how to debug it.

Here are my models; which are also Room objects

data class JobPW(
    @Embedded val job: Job,
    @Relation(
        parentColumn = "job_id",
        entityColumn = "worker_id",
        associateBy = Junction(JobWorkerCrossRef::class)
    )
    val workers: List<Worker>,

    @Relation(
        entity = Poster::class,
        parentColumn = "job_id",
        entityColumn = "job_id_map"
    )
    val posters: List<PosterWithWorker>
) : Serializable
data class PosterWithWorker(
    @Embedded val poster: Poster,
    @Relation(
        parentColumn = "worker_id",
        entityColumn = "worker_id",
        associateBy = Junction(
            value = PosterWorkerCrossRef::class,
            parentColumn = "poster_id_map",
            entityColumn = "worker_id_map"
        )
    )
    val worker:  List<Worker>
) : Serializa
package com.davidozersky.posterpal.database.entities

import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.PrimaryKey
import java.io.Serializable

@Entity(
    foreignKeys = [

        ForeignKey(
            entity = Owner::class,
            parentColumns = ["owner_id"],
            childColumns = ["owner_id_map"],
            onDelete = ForeignKey.CASCADE,
            onUpdate = ForeignKey.CASCADE
        )
    ]
)
data class Job (

    @PrimaryKey val job_id: Long,
    val status: Int,
    @ColumnInfo(index = true)
    val owner_id_map: Long,
    val title: String,
    val city: String
) : Serializable
package com.davidozersky.posterpal.database.entities

import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import java.io.Serializable

@Entity
class Worker(
    @PrimaryKey
    @ColumnInfo(name = "worker_id")
    val worker_id: Long,
    val workerName: String,
) : Serializable

Poster

package com.davidozersky.posterpal.database.entities

import androidx.room.*

@Entity(
    foreignKeys = [
        ForeignKey(
            entity = Job::class,
            parentColumns = ["job_id"],
            childColumns = ["job_id_map"],
            onDelete = ForeignKey.CASCADE,
            onUpdate = ForeignKey.CASCADE
        )
    ]
)
data class Poster (
    @PrimaryKey val poster_id: Long,
    @ColumnInfo(index = true)
    val job_id_map: Long,
    val qr_code: String,
    val worker_id: Long,
    val latitude: Double,
    val longitude: Double,
    val locationDescription: String,
    )

I tried using OkHttp directly with the HttpLogger and the call responds with a 200 and it logs out the same json

[{"job":{"city":"Toronto","job_id":1,"owner_id_map":1,"status":0,"title":"500 Posters in Toronto"},"posters":[{"poster":{"job_id_map":2,"latitude":43.64526533333377,"locationDescription":"Union Station","longitude":-79.38060468539248,"poster_id":1,"qr_code":"code2","worker_id":1},"worker":[{"workerName":"Noris","worker_id":1}]},{"poster":{"job_id_map":2,"latitude":43.64295669900133,"locationDescription":"Queen and Spadina","longitude":-79.39368099382459,"poster_id":2,"qr_code":"code3","worker_id":2},"worker":[{"workerName":"Jack","worker_id":2}]},{"poster":{"job_id_map":3,"latitude":43.76892443268843,"locationDescription":"North York Civic Center","longitude":-79.41292484238735,"poster_id":3,"qr_code":"code4","worker_id":3},"worker":[{"workerName":"Nicky","worker_id":3}]}],"workers":[{"workerName":"Noris","worker_id":1},{"workerName":"Jack","worker_id":2},{"workerName":"Nicky","worker_id":3},{"workerName":"Davis","worker_id":4},{"workerName":"Argyle","worker_id":5},{"workerName":"James","worker_id":6}]},{"job":{"city":"Montreal","job_id":2,"owner_id_map":1,"status":1,"title":"20 Posters in Montreal"},"workers":[{"workerName":"Noris","worker_id":1},{"workerName":"Jack","worker_id":2},{"workerName":"Nicky","worker_id":3},{"workerName":"Davis","worker_id":4},{"workerName":"Argyle","worker_id":5},{"workerName":"James","worker_id":6}]},{"job":{"city":"Ottawa","job_id":3,"owner_id_map":2,"status":2,"title":"350 Posters in Ottawa"},"posters":[{"poster":{"job_id_map":4,"latitude":43.63904362027761,"locationDescription":"Harbourfront","longitude":-79.38193214610098,"poster_id":4,"qr_code":"code5","worker_id":0}},{"poster":{"job_id_map":5,"latitude":43.68347235901327,"locationDescription":"Bathurst and St Clair","longitude":-79.41851002097324,"poster_id":5,"qr_code":"code6","worker_id":0}},{"poster":{"job_id_map":2,"latitude":43.6958481647278,"locationDescription":"Dufferin and Eglinton","longitude":-79.45035794609939,"poster_id":6,"qr_code":"code7","worker_id":0}}],"workers":[{"workerName":"Noris","worker_id":1},{"workerName":"Jack","worker_id":2},{"workerName":"Nicky","worker_id":3},{"workerName":"Davis","worker_id":4},{"workerName":"Argyle","worker_id":5},{"workerName":"James","worker_id":6}]},{"job":{"city":"Calgary","job_id":4,"owner_id_map":2,"status":0,"title":"10 Posters in Calgary"},"posters":[{"poster":{"job_id_map":2,"latitude":43.64526533333377,"locationDescription":"Union Station","longitude":-79.38060468539248,"poster_id":1,"qr_code":"code2","worker_id":1},"worker":[{"workerName":"Noris","worker_id":1}]},{"poster":{"job_id_map":2,"latitude":43.64295669900133,"locationDescription":"Queen and Spadina","longitude":-79.39368099382459,"poster_id":2,"qr_code":"code3","worker_id":2},"worker":[{"workerName":"Jack","worker_id":2}]},{"poster":{"job_id_map":3,"latitude":43.76892443268843,"locationDescription":"North York Civic Center","longitude":-79.41292484238735,"poster_id":3,"qr_code":"code4","worker_id":3},"worker":[{"workerName":"Nicky","worker_id":3}]}],"workers":[{"workerName":"Noris","worker_id":1},{"workerName":"Jack","worker_id":2},{"workerName":"Nicky","worker_id":3},{"workerName":"Davis","worker_id":4},{"workerName":"Argyle","worker_id":5},{"workerName":"James","worker_id":6}]}]
1

There are 1 answers

2
Shift Delete On
@GET("/jobs.json")
suspend fun getJobsList() : Response<List<JobPW>>

why did you wrap your list with a Response class? Maybe Your Response Class is not the same as the retrofit.Response Class. Check your import in that case.

Second, check the Interceptors you add to your OkHttp and see if you didn't corrupt the response.

If they didn't work for you tell how you get the JsonObject from Retrofit and show that code.

Edit

It's a bad practice to use the same class for your entity and response. I write to you a response class. plz try this and let me know what happened.

data class JobsResponseItem(

    @field:SerializedName("posters")
    val posters: List<PostersItem>,

    @field:SerializedName("job")
    val job: Job,

    @field:SerializedName("workers")
    val workers: List<WorkersItem>
){

    data class PostersItem(

        @field:SerializedName("worker")
        val worker: List<WorkerItem>,

        @field:SerializedName("poster")
        val poster: Poster
    ){

        data class WorkerItem(

            @field:SerializedName("workerName")
            val workerName: String,

            @field:SerializedName("worker_id")
            val workerId: Int
        )

        data class Poster(

            @field:SerializedName("latitude")
            val latitude: Double?,

            @field:SerializedName("locationDescription")
            val locationDescription: String,

            @field:SerializedName("job_id_map")
            val jobIdMap: Int,

            @field:SerializedName("qr_code")
            val qrCode: String,

            @field:SerializedName("poster_id")
            val posterId: Int,

            @field:SerializedName("longitude")
            val longitude: Double?,

            @field:SerializedName("worker_id")
            val workerId: Int
        )

    }

    data class Job(

        @field:SerializedName("owner_id_map")
        val ownerIdMap: Int,

        @field:SerializedName("city")
        val city: String,

        @field:SerializedName("job_id")
        val jobId: Int,

        @field:SerializedName("title")
        val title: String,

        @field:SerializedName("status")
        val status: Int
    )

    data class WorkersItem(

        @field:SerializedName("workerName")
        val workerName: String,

        @field:SerializedName("worker_id")
        val workerId: Int
    )
}

and update your restApi as below:

@GET("/jobs.json")
suspend fun getJobsList() : Response<List<JobsResponseItem>>