8

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.

2
  • Could you please try to add default value in your Person data model. Hope this will help you Commented Oct 11, 2019 at 2:21
  • That worked, but I'm hoping for something that won't require me to set default values across all of my data models. The real world application is MUCH more complex. If you post this as an answer, and I don't get a better one, then I'll accept this. Thank you very much! Commented Oct 11, 2019 at 2:25

3 Answers 3

15

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

Sign up to request clarification or add additional context in comments.

2 Comments

Thank you. I may have to resort to this eventually. I'm hoping to find a setting that will relax this requirement. For example, Gson has no such requirement. If I do wind up using this, I will accept this answer.
Sure, no problem. You can also try vararg for similar types of parameter, though it loses key->value facilities
3

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

Comments

0

For others who has already made the property as nullable and still get the issue, set explicitNulls to false in your JsonBuilder

@OptIn(ExperimentalSerializationApi::class)
val CustomJson = Json {
    ignoreUnknownKeys = true
    isLenient = true
    explicitNulls = false
}

// usage
CustomJson.decodeFromString<Class>(jsonString)

enter image description here

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.