使用 Retrofit 处理 Kotlin 序列化 MissingFieldException

Posted

技术标签:

【中文标题】使用 Retrofit 处理 Kotlin 序列化 MissingFieldException【英文标题】:Handling Kotlin Serialization MissingFieldException with Retrofit 【发布时间】:2020-02-08 12:21:28 【问题描述】:

我将kotlinx.serialization 与retrofit 结合使用。我收到的 json 响应会根据它包含的属性而有所不同。在大多数情况下,我的应用程序中的数据模型包含的字段比我在响应中收到的要多。我无法控制这个,所以我需要在代码中处理它。

在这种情况下,Kotlinx.serialization 会抛出 MissingFieldException。我知道在使用 Json.parse 时,您可以将其包装在 try-catch 块中并忽略此类错误。但是由于我使用的是 Retrofit,所以我看不到使用这种方法的方法:

WebService.kt

interface WebService 
    @GET("person.json")
    fun getPerson(): Call<MainActivity.Person>

MainActivity.kt

class MainActivity : AppCompatActivity() 

    @Serializable
    data class Person(val name: String, val species: String, val missing: String)

    @UnstableDefault
    override fun onCreate(savedInstanceState: Bundle?) 
        val mediaType = "application/json".toMediaTypeOrNull()
        mediaType?.let 
            retrofit = Retrofit.Builder()
                .addConverterFactory(Json.nonstrict.asConverterFactory(it))
                .baseUrl(baseUrl)
                .build()
        

        webService = retrofit.create(WebService::class.java)

        GlobalScope.launch 
            val person = fetchPerson(webService)
        
    

    private suspend fun fetchPerson(webService: WebService): Person 
        return suspendCancellableCoroutine  cont ->
            webService.getPerson()
                .enqueue(object : Callback<Person> 
                    override fun onFailure(call: Call<Person>, t: Throwable) 
                        Log.e(t.toString(), "Unable to get api response")
                        cont.cancel(t)
                    

                    override fun onResponse(
                        call: Call<Person>,
                        response: Response<Person>
                    ) 
                        if (response.isSuccessful) 
                            response.body()?.let  cont.resume(it) 
                         else 
                            cont.cancel(IOException("$response.code(): $response.errorBody()"))
                        
                    
                )
        
    

json 响应(在这个虚构的示例中)故意省略了“缺失”字段:

"name":"me", "species":"superhuman"

由于该 json 不包含数据类中的 missing 字段,因此应用程序崩溃并抛出 MissingFieldException。我想知道如何在改造案例中避免这个问题。

感谢您的帮助。

【问题讨论】:

您能否尝试在您的Person 数据模型中添加默认值。希望对你有帮助 这行得通,但我希望不需要我在所有数据模型中设置默认值。现实世界的应用程序要复杂得多。如果您将此作为答案发布,而我没有得到更好的答案,那么我会接受。非常感谢! 【参考方案1】:

实际上它无法从 json 创建 Person 对象,因为您的 Person 类构造函数需要 3 个值。您必须满足此要求才能创建对象。

解决此问题的一种可能解决方案是在 Kotlin 中使用默认值,如下所示:

data class Person(
    var name: String="", 
    var species: String="", 
    var missing: String="") 

另一种解决方案是使用具有不同参数的多个构造函数,但是由于您提到它可能会随着时间的推移而有所不同,因此该解决方案可能并不方便。谢谢

【讨论】:

谢谢。我最终可能不得不诉诸于此。我希望找到一个可以放宽这个要求的设置。例如,Gson 没有这样的要求。如果我最终使用这个,我会接受这个答案。 当然,没问题。您也可以尝试vararg 获取类似类型的参数,但它会丢失键->值功能【参考方案2】:

kotlinx.serialization-1.3.0开始,可以创建Json对象为Json explicitNulls = false 。这将有助于序列化具有不同字段的响应,而无需抛出 MissingFieldException 并且无需传递默认值。

Reference

【讨论】:

以上是关于使用 Retrofit 处理 Kotlin 序列化 MissingFieldException的主要内容,如果未能解决你的问题,请参考以下文章

无法使用 Kotlin Retrofit 将数据从 Android 插入到 Mysql [重复]

在 Kotlin 中使用 Moshi 和 Retrofit 解析具有增量对象名称的 JSON

Retrofit + 协程封装,如何优雅的去掉try catch?

Android Kotlin Retrofit Post Request 输入的数据未发送

使用 Retrofit2(kotlin) 的 CURL 请求

使用 Retrofit 和 Kotlin 使用多态 Json