Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Handling Kotlin Serialization MissingFieldException with Retrofit

I'm using kotlinx.serialization in conjunction with retrofit. The json response that I receive will vary in terms of what attributes it will contain. In most cases, the data model in my app has more fields than I will receive in the response. I cannot control this, so I need to handle it in code.

Kotlinx.serialization throws a MissingFieldException in such cases. I know that when using Json.parse you can wrap it in a try-catch block and ignore such errors. But since I am using Retrofit, I don't see a way to use that approach:

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()}"))
                        }
                    }
                })
        }
    }
}

The json response (in this made up example) intentionally omits the 'missing' field:

{"name":"me", "species":"superhuman"}

Since that json does not contain the missing field from the data class, the app crashes and throws the MissingFieldException. I'm wondering how to avoid this issue in the Retrofit case.

Thanks for any help.

like image 752
AndroidDev Avatar asked Oct 11 '19 02:10

AndroidDev


2 Answers

Actually it can't create Person object from json as your Person class constructor wants 3 value. You have to fulfill this requirement to create object.

One possible solution to resolve this is to use default value in Kotlin like this:

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

Another solution is to use multiple constructor with varying no of parameters but since you mentioned it may differ over time, so this solution may not handy. Thanks

like image 67
Md. Asaduzzaman Avatar answered Nov 15 '22 03:11

Md. Asaduzzaman


Starting from kotlinx.serialization-1.3.0, you can create the Json object as Json { explicitNulls = false }. This will help in serializing response with varying fields without throwing the MissingFieldException and without the need of passing default values.

Reference

like image 41
Waseem Avatar answered Nov 15 '22 05:11

Waseem