【问题标题】:Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path Kotlin Coroutines with MVVM & Retrofit预期为 BEGIN_ARRAY,但在第 1 行第 2 列路径 Kotlin Coroutines with MVVM & Retrofit 为 BEGIN_OBJECT
【发布时间】:2021-05-20 09:05:10
【问题描述】:

我正在使用 Kotlin 的改造和使用 MVVM 模式的协程。这是我第一次使用改造和 kotlin。我的问题是我正在调用新闻 api 并收到此错误,即使我已经尝试自己解决我的问题但没有得到任何适当的解决方案。

Json 响应:

{
"status": "ok",
"totalResults": 3923,
-"articles": [
-{
-"source": {
"id": null,
"name": "Finextra"
},
"author": "Editorial Team",
"title": "Solaris Digital Assets wins Bitwala as digital asset custody partner",
"description": "Solaris Digital Assets GmbH, a 100% subsidiary of Solarisbank AG, today announced that it has won Bitwala, Germany’s crypto-banking flagship company, as a partner for its digital asset custody solution.",
"url": "https://www.finextra.com/pressarticle/85033/solaris-digital-assets-wins-bitwala-as-digital-asset-custody-partner",
"urlToImage": "https://www.finextra.com/about/finextra-logo-alt-16-9.jpg",
"publishedAt": "2020-11-17T14:28:00Z",
"content": "Solaris Digital Assets GmbH, a 100% subsidiary of Solarisbank AG, today announced that it has won Bitwala, Germanys crypto-banking flagship company, as a partner for its digital asset custody solutio… [+3321 chars]"
},
-{
-"source": {
"id": null,
"name": "Seeking Alpha"
},
"author": "Ophelia Research",
"title": "Power Corporation Of Canada Is Still A Buy",
"description": "Wealthsimple continues to grow through social media platforms and referral incentives. Power Corporation of Canada continues to grow its investments in start-ups.",
"url": "https://seekingalpha.com/article/4389643-power-corporation-of-canada-is-still-buy",
"urlToImage": "https://static2.seekingalpha.com/uploads/2020/11/15/saupload_EWZQEwLYN4dxnan8QPFcRnpuNy_nvcN-PV5mrbjb97co4v9-QgGK8ZN8UqwxzO3oSPoiDkwnvSMFsyqKGu06-S1TGHHydTAz8VkQXaY5-FjSbTa5-qzCROck4sPk2ZeSD6rYIL1P.png",
"publishedAt": "2020-11-17T14:21:26Z",
"content": "Power Corporation of Canada (OTCPK:PWCDF) is a diversified financial services company that pays out solid dividends due to strong established brands and still has the potential for growth given its i… [+6824 chars]"
}]}

改造建造者:

object RetrofitBuilder {

    private const val BASE_URL = "http://newsapi.org/v2/"

    private fun getRetrofit(): Retrofit {
        return Retrofit.Builder()
            .baseUrl(BASE_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .build() //Doesn't require the adapter
    }

    val apiService: ApiService = getRetrofit().create(ApiService::class.java)
}

API接口:

interface ApiService {
    @GET("sources/apikey")
    suspend fun getTopHeadlines(): Model
}

API 助手:

suspend fun getTopHeadlines() = apiService.getTopHeadlines()

主存储库:

suspend fun getTopHeadlines() = apiHelper.getTopHeadlines()

ViewModelFactory:

override fun <T : ViewModel?> create(modelClass: Class<T>): T {
    if (modelClass.isAssignableFrom(MainViewModel::class.java)) {
        return MainViewModel(MainRepository(apiHelper)) as T
    }
    throw IllegalArgumentException("Unknown class name")
}

主视图模型:

fun getTopHeadlines() = liveData(Dispatchers.IO) {
    emit(Resource.loading(data = null))
    try {
        emit(Resource.success(data = mainRepository.getTopHeadlines()))
    } catch (exception: Exception) {
        emit(Resource.error(data = null, msg = exception.message ?: "Error Occurred!"))
    }
}

模型类:

data class Model(val status: String,val totalResults: Int,val articles: List<Article>)

文章分类:

data class Article(
    val source: Source,
    val author: String,
    val content: String,
    val description: String,
    val publishedAt: String,
    val title: String,
    val url: String,
    val urlToImage: String
)

源类:

data class Source(
    val id: Any,
    val name: String
)

主要活动:

viewModel.getTopHeadlines().observe(this, Observer {
    it?.let { resource ->
        when (resource.status) {
            Status.SUCCESS -> {
                Log.e("MainClass","Data caught: "+it.message);
                // resource.data?.let { users -> retrieveList(users) }
            }
            Status.ERROR -> {
                Log.e("MainClass","Exception caught: "+it.message);
                Toast.makeText(this, it.message, Toast.LENGTH_LONG).show()
            }
            Status.LOADING -> {
            }
        }
    }
})

【问题讨论】:

  • mmm...您获取模型数据的方法看起来不错,因为 json 以“{”开头,所以它是一个对象,而不是一个数组...所以请使用 Verbose 检查日志带有键“okhttp”的模式,您的响应是 200... 并使用 try catch 和改造 try { yourCall } catch(例如:HttpException 和 IOException 来处理互联网连接,如果你愿意)

标签: android kotlin


【解决方案1】:

好的。从this documentation 和你的代码来看,这里有很多问题,所以我将在下面详细介绍。

  1. 您创建的Model 类:

    • 这是我为您提供的 Json 生成的 POJO 结构..

类似这样的:

data class Base(
    @SerializedName("status") val status: String,
    @SerializedName("totalResults") val totalResults: Int,
    @SerializedName("articles") val articles: List<Articles>
)

data class Articles(
    @SerializedName("source") val source: Source,
    @SerializedName("author") val author: String,
    @SerializedName("title") val title: String,
    @SerializedName("description") val description: String,
    @SerializedName("url") val url: String,
    @SerializedName("urlToImage") val urlToImage: String,
    @SerializedName("publishedAt") val publishedAt: String,
    @SerializedName("content") val content: String
)

data class Source(
    @SerializedName("id") val id: String,
    @SerializedName("name") val name: String
)

模型类完成。继续你的改造代码。

  1. 您的Retrofit 实例:

    • @GET(sources/apikey) 行完全错误。文档中不存在类似的东西,您将无法从中得到任何东西。为了得到你需要的东西,你需要参考top-headlinessourceseverything
    • 您的object RetrofitBuilder 可以通过某些方式进行简化。
    • 您需要查询 ApiKey 以及您没有执行的请求
    • 您需要查询 Country 以及您没有做的请求

然后让我们应用更改。 (我在这里使用top-headlines,因为它适合我们的模型类):

// This is much simplified version of what you have written.
interface NewsApi {
    // Here we use correct API endpoint.
    @GET("top-headlines")
    suspend fun getTopHeadlines(
        // This is how you Query necessary parameters for APIs
        // In our case, we need to query Country + apiKey
        @Query("country") country: String = "us",
        @Query("apiKey") apiKey: String = "%InsertYourApiKey%"
    ): Response<Base> // Response<Base> is simply a class that allows you to read API's response codes, body and other details that you might need for processing response information.

    companion object {
        fun getInstance(): NewsApi {
            return Retrofit.Builder()
                .baseUrl("http://newsapi.org/v2/")
                .addConverterFactory(GsonConverterFactory.create())
                .build()
                .create(NewsApi::class.java) // Creation doesn't have to be separate, you can have it in here for more concise code.
        }
    }
}

这些改变之后,你要做的就是:

  1. 将您的 API 密钥插入我写过%InsertYourApiKey 的界面中
  2. 在您需要的任何地方使用以下代码:

...

val newsApi = NewsApi.getInstance()
val response = newsApi.getTopHeadlines()

// Do whatever you need with your response.

【讨论】:

  • "从 Gson 的角度来看是不正确的。每个字段都需要用 @SerializedName 进行注释"。你实际上没有。 GsonConverterFactory 将根据类字段名称自行管理它。您只需要确保它们与您的 Json 匹配。所以如果你想在你的类中以不同的方式命名字段,使用@SerializedName 会更安全,但不是必需的
  • 一切都已经正确,比如端点等,但我错过了“序列化”注释。感谢您指出这一点。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-08-05
  • 1970-01-01
  • 2021-09-23
  • 2021-04-28
  • 2018-05-23
  • 1970-01-01
  • 2020-01-29
相关资源
最近更新 更多