使用 Retrofit 处理 Kotlin 序列化 MissingFieldException
Handling Kotlin Serialization MissingFieldException with Retrofit
我正在使用 kotlinx.serialization in conjunction with 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 响应(在这个虚构的示例中)有意省略了 'missing' 字段:
{"name":"me", "species":"superhuman"}
由于 json 不包含数据 class 中的 missing
字段,应用程序崩溃并抛出 MissingFieldException
。我想知道如何在 Retrofit 案例中避免这个问题。
感谢您的帮助。
实际上它无法从 json 创建 Person
对象,因为您的 Person
class 构造函数需要 3 个值。您必须满足此要求才能创建对象。
一个可能的解决方案是在 Kotlin 中使用默认值,如下所示:
data class Person(
var name: String="",
var species: String="",
var missing: String="")
另一种解决方案是使用参数数量不同的多个构造函数,但由于您提到它可能会随着时间的推移而有所不同,因此该解决方案可能不方便。谢谢
从 kotlinx.serialization-1.3.0
开始,您可以将 Json 对象创建为 Json { explicitNulls = false }
。这将有助于序列化具有不同字段的响应,而无需抛出 MissingFieldException
并且不需要传递默认值。
我正在使用 kotlinx.serialization in conjunction with 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 响应(在这个虚构的示例中)有意省略了 'missing' 字段:
{"name":"me", "species":"superhuman"}
由于 json 不包含数据 class 中的 missing
字段,应用程序崩溃并抛出 MissingFieldException
。我想知道如何在 Retrofit 案例中避免这个问题。
感谢您的帮助。
实际上它无法从 json 创建 Person
对象,因为您的 Person
class 构造函数需要 3 个值。您必须满足此要求才能创建对象。
一个可能的解决方案是在 Kotlin 中使用默认值,如下所示:
data class Person(
var name: String="",
var species: String="",
var missing: String="")
另一种解决方案是使用参数数量不同的多个构造函数,但由于您提到它可能会随着时间的推移而有所不同,因此该解决方案可能不方便。谢谢
从 kotlinx.serialization-1.3.0
开始,您可以将 Json 对象创建为 Json { explicitNulls = false }
。这将有助于序列化具有不同字段的响应,而无需抛出 MissingFieldException
并且不需要传递默认值。