首页 > 解决方案 > Json 异常:应为 BEGIN_OBJECT 但为 BEGIN_ARRAY

问题描述

我花了 3 个小时,我找不到解析错误的原因。

我有这个 JSON

[
  {
    "id": "WMWSW31030T222518",
    "modelName": "MINI",
    "name": "Vanessa",
    "make": "BMW",
    "latitude": 48.134557,
    "longitude": 11.576921,
    "carImageUrl": "https://cdn.sixt.io/codingtask/images/mini.png"
  },
  {
    "id": "WMWSU31070T077232",
    "modelName": "MINI",
    "name": "Regine",
    "make": "BMW",
    "latitude": 48.114988,
    "longitude": 11.598359,
    "carImageUrl": "https://cdn.sixt.io/codingtask/images/mini.png"
  }
]

解析后我得到了这个错误

An error happened: com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 2

这是我的代码

--存储库--

interface CarRepository {
    suspend fun getCars(): LiveData<Resource<List<Car>>>
}

class CarRepositoryImpl(private val datasource: CarDatasource,
                        private val dao: CarDao): CarRepository{
    override suspend fun getCars(): LiveData<Resource<List<Car>>> {
        return object : NetworkBoundResource<List<Car>, ApiResult<Car>>() {

            override fun processResponse(response: ApiResult<Car>): List<Car>
                    = response.items

            override suspend fun saveCallResults(items: List<Car>)
                    = dao.save(items)

            override fun shouldFetch(data: List<Car>?): Boolean
                    = data == null || data.isEmpty()

            override suspend fun loadFromDb(): List<Car>
                    = dao.getCars()

            override fun createCallAsync(): Deferred<ApiResult<Car>>
                    = datasource.fetchCars()

        }.build().asLiveData()
    }

}

- 汽车服务 -

interface CarService {

    @GET("cars")
    fun fetchCars(): Deferred<ApiResult<Car>>
}

--ApiResult--

data class ApiResult<T>(val items: List<T>)

--NetworkBoundResource--

abstract class NetworkBoundResource<ResultType, RequestType> {

    private val result = MutableLiveData<Resource<ResultType>>()
    private val supervisorJob = SupervisorJob()

    suspend fun build(): NetworkBoundResource<ResultType, RequestType> {
        withContext(Dispatchers.Main) { result.value =
            Resource.loading(null)
        }
        CoroutineScope(coroutineContext).launch(supervisorJob) {
            val dbResult = loadFromDb()
            if (shouldFetch(dbResult)) {
                try {
                    fetchFromNetwork(dbResult)
                } catch (e: Exception) {
                    Log.e("NetworkBoundResource", "An error happened: $e")
                    setValue(Resource.error(e, loadFromDb()))
                }
            } else {
                Log.d(NetworkBoundResource::class.java.name, "Return data from local database")
                setValue(Resource.success(dbResult))
            }
        }
        return this
    }

    fun asLiveData() = result as LiveData<Resource<ResultType>>

    // ---

    private suspend fun fetchFromNetwork(dbResult: ResultType) {
        Log.d(NetworkBoundResource::class.java.name, "Fetch data from network")
        setValue(Resource.loading(dbResult)) // Dispatch latest value quickly (UX purpose)
        val apiResponse = createCallAsync().await()
        Log.e(NetworkBoundResource::class.java.name, "Data fetched from network")
        saveCallResults(processResponse(apiResponse))
        setValue(Resource.success(loadFromDb()))
    }

    @MainThread
    private fun setValue(newValue: Resource<ResultType>) {
        Log.d(NetworkBoundResource::class.java.name, "Resource: "+newValue)
        if (result.value != newValue) result.postValue(newValue)
    }

    @WorkerThread
    protected abstract fun processResponse(response: RequestType): ResultType

    @WorkerThread
    protected abstract suspend fun saveCallResults(items: ResultType)

    @MainThread
    protected abstract fun shouldFetch(data: ResultType?): Boolean

    @MainThread
    protected abstract suspend fun loadFromDb(): ResultType

    @MainThread
    protected abstract fun createCallAsync(): Deferred<RequestType>

--资源--

data class Resource<out T>(val status: Status, val data: T?, val error: Throwable?) {
    companion object {
        fun <T> success(data: T?): Resource<T> {
            return Resource(
                Status.SUCCESS,
                data,
                null
            )
        }

        fun <T> error(error: Throwable, data: T?): Resource<T> {
            return Resource(
                Status.ERROR,
                data,
                error
            )
        }

        fun <T> loading(data: T?): Resource<T> {
            return Resource(
                Status.LOADING,
                data,
                null
            )
        }
    }

    enum class Status {
        SUCCESS,
        ERROR,
        LOADING
    }
}

谁能告诉我为什么解析器失败了?

标签: androidkotlingsonretrofitkotlin-coroutines

解决方案


错误在这里,您应该使用列表而不是 ApiResult,因为 ApiResult 是具有列表的对象,而 GSON 尝试解析对象并查找具有属性名称项的列表。

//change to List<Car>
interface CarService {

    @GET("cars")
    fun fetchCars(): Deferred<List<Car>>
}

推荐阅读