在 MVVM 架构中格式化来自改造 API 的嵌套 JSON 响应 - Kotlin

Posted

技术标签:

【中文标题】在 MVVM 架构中格式化来自改造 API 的嵌套 JSON 响应 - Kotlin【英文标题】:Formatting the Nested JSON response from retrofit API in MVVM Architecture - Kotlin 【发布时间】:2021-10-30 21:30:18 【问题描述】:

我是 kotlin 和 MVVM 的新手,我已经解决这个问题一个星期了,即使在互联网上搜索了一些代码后也没有任何想法。

我正在尝试根据我的需要编辑或修改改造响应(以观察特定类型;说“sf”)并忽略其他不需要的数据。我正在使用可变 livedata 来获取和更新从改造响应到 recylerview 的 JSON 数据。

这里是 JSON 数据的链接:http://www.nactem.ac.uk/software/acromine/dictionary.py?sf=HMM

3 基于 JSON 响应的数据类:

data class sf(

    @SerializedName("sf")
    @Expose
    val sf : String? = null,

    @SerializedName("lfs")
    @Expose
    val lfs : List<lfs>? = null,
)
data class lfs(

    @SerializedName("lf")
    @Expose
    var lf : String? = null,

    @SerializedName("freq")
    @Expose
    var freq : Int? = null,

    @SerializedName("since")
    @Expose
    var since : Int? = null,

    @SerializedName("vars")
    @Expose
    var vars : List<vars>? = null,

) : Serializable
class vars (

    @SerializedName("lf")
    @Expose
    var lf : String? = null,

    @SerializedName("freq")
    @Expose
    var freq : Int? = null,

    @SerializedName("since")
    @Expose
    var since : Int?

): Serializable

活动中的代码:

listUsers = mutableListOf()

        adapter = WordAdapters(this, listUsers )
        recyclerView.adapter = adapter

        wordViewModel = ViewModelProviders.of(this,
            WordViewModelFactory(this)).get(WordsViewModel::class.java)
        wordViewModel!!.getData().observe(this,  t: ArrayList<sf>? ->
            listUsers.clear()
            t?.let  listUsers.addAll(it)
            
            adapter.notifyDataSetChanged()


  )

视图模型:

class WordsViewModel ( context: Context) : ViewModel() 

private var listData = MutableLiveData<ArrayList<sf>>()

init 
    val wordRepository: WordsRepository by lazy 
            WordsRepository
        
        //if (context.isInternetAvailable()) 
            listData = wordRepository.getMutableLiveData(context)
       // 
    

    fun getData(): MutableLiveData<ArrayList<sf>> 
        return listData
     

存储库:

    object WordsRepository 
    
        var call: Call<MutableList<sf>>? = null
        fun getMutableLiveData(context: Context) : MutableLiveData<ArrayList<sf>> 
    
            val mutableLiveData = MutableLiveData<ArrayList<sf>>()
    
            //context.showProgressBar()
            call = NetworkApiClient.apiService.getWordsMatching("HMM")
            call!!.enqueue(object : Callback<MutableList<sf>> 
                override fun onFailure(call: Call<MutableList<sf>>, t: Throwable) 
                    //hideProgressBar()
                    Log.e("error", t.localizedMessage.toString())
                
                override fun onResponse(call: Call<MutableList<sf>>, response: 
                            Response<MutableList<sf>>)
                
                    //hideProgressBar()
                    if (!response.isSuccessful)
                        Log.e("Code " , response.code().toString());
                        return
                    
                    val raw: okhttp3.Response = response.raw()
                    val usersResponse : MutableList<sf>? = response.body()
                   /* if (usersResponse != null) 
                        for( movie in usersResponse[0].lfs!!)
                            Log.v("MainActivity", movie.vars.toString())
                        
                    */
                    Log.e("Output : ", usersResponse.toString())
                    usersResponse?.let  mutableLiveData.value = it as ArrayList<sf> 
                
    
            )
    
            return mutableLiveData
        
    

这是 JSON 的基本结构:这里“sf”是一个字符串,lfs 是数组,根据这个 JSON 响应链接提供的我得到 8 个 lfs 数组,但目前解析后的 recyclecount 是 1,这在适配器 itemcount 方法,所以我在 recylerview 中显示一行,其余的被忽略。

JSON 响应:

[

"sf":"HMM",
"lfs":[

"lf":"heavy meromyosin",
"freq":267,
"since":1971,
"vars":[

"lf":"heavy meromyosin",
"freq":244,
"since":1971
,

"lf":"Heavy meromyosin",
"freq":12,
"since":1975
,

"lf":"H-meromyosin",
"freq":5,
"since":1975
,

"lf":"heavy-meromyosin",
"freq":4,
"since":1977
,

"lf":"heavy meromyosin",
"freq":1,
"since":1976
,

"lf":"H-Meromyosin",
"freq":1,
"since":1976

]
,

我想在响应后忽略“sf”字符串并解析存在于“lfs”的“sf”下的ArrayList,因此我需要基于“lfs”显示数据。

可变实时数据不接受除 sf 以外的任何其他类型,因为我将观察者放在它上面。

【问题讨论】:

你遇到了什么错误? 我正在观察作为根对象的“sf”数据模型,但我试图在“sf”下获取数组列表“lfs”,这是发生类型转换问题的地方,实际上返回 null数据到recyclerview。 如果我使用当前的数据模型,我会得到数据,但如果更改数据模型,它会抛出类似“预期 BEGIN_ARRAY 但是 BEGIN_OBJECT”的错误 - 我正在使用 retrofit2 请修剪您的代码,以便更容易找到您的问题。请按照以下指南创建minimal reproducible example。 【参考方案1】:

在您发布的 json 中,只有一个父项(一个 sf ),但您实际上是在尝试传递 8 个 lfs 子项。你必须在某个地方执行这样的转换,它可以直接在网络调用上,像这样:

usersResponse?.let mutableLiveData.value = it[0].lfs as ArrayList

考虑两件事:

    在查找第一项之前检查“it”是否为空会更好。

    这只有在父数组上总是只有一个项目时才有效(这听起来很奇怪,因为如果是这种情况,那么服务应该返回一个对象,而不是一个列表,作为 json 的根。如果你将收到多个对象,您必须将响应映射到单个 lfs 列表中。类似于(伪代码,因为我来自手机):

    It.map(item -> item.lfs)

【讨论】:

是的,这个 json 响应总是有一个父级,这是肯定的。第一个:我已经尝试对它的对象进行空检查第二个:铸造将不可能,因为我只观察 sf,如果我尝试观察“lfs”,它只会返回 null。 usersResponse?.let mutableLiveData.value = it[0].lfs as ArrayList ,此语句不会编译为 im oberving "sf",无法转换 Sergio Pardo - 感谢您的回复,我已经尝试了上述建议,但它并没有帮助我。

以上是关于在 MVVM 架构中格式化来自改造 API 的嵌套 JSON 响应 - Kotlin的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 ViewModel 和 LiveData 进行改造 API 调用

recyclerView.addOnScrollListener - “使用 MVVM 改造分页”正在加载相同的响应/列表

使用改造对 mvvm 中的 Rxjava 进行错误处理

使用 MockWebServer 模拟嵌套的改造 api 调用

使用 MockWebServer 模拟嵌套的改造 api 调用

使用改造从 api 获取数据时出错