Search code examples
androidkotlinserializationgsonretrofit2

Gson serialization error with val in kotlin


I am using Gson v2.8.2 and Retrofit v2.3.0 and I have two classes, Answer.kt:

open class Answer(@SerializedName("answer")
                  var text: String,
                  val id: Int)

and AnswerSummary.kt:

class AnswerSummary(val answer: Answer) : Answer(answer.text, answer.id) {
    val percent: Int = 0
}

I am using it in a list (in Java, still migrating to Kotlin):

public List<AnswerSummary> getAnswerSummaries() {
            return answerSummaries;
        }

I get an error when val is not removed: class AnswerSummary(val answer: Answer) ...:

java.lang.IllegalArgumentException: Unable to create converter for class com.name.app.model.response.AnswerResponse
....
Caused by java.lang.IllegalArgumentException: class com.name.app.model.pojo.AnswerSummary declares multiple JSON fields named answer
       at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.getBoundFields(ReflectiveTypeAdapterFactory.java:170)
       at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.create(ReflectiveTypeAdapterFactory.java:100)
       at com.google.gson.Gson.getAdapter(Gson.java:423)
       at com.google.gson.internal.bind.CollectionTypeAdapterFactory.create(CollectionTypeAdapterFactory.java:53)
       ....

After removing 'val': class AnswerSummary(answer: Answer) ..., the error goes away. Why is this, since I only annotated one field? - @SerializedName("answer") var text: String.

I found a way to circumvent the crash while still maintaining immutability ('val') - By using a different variable name: class AnswerSummary(val ans: Answer) .... What is happening behind the background - is it related to the fact that the @SerializedName field value is the same as the variable name?


Solution

  • Try to rename the answer parameter:

    class AnswerSummary(val ans: Answer) : Answer(ans.text, ans.id) { ... }
    

    or use answer as parameter, not property:

    class AnswerSummary(answer: Answer) : Answer(answer.text, answer.id) { ... }
    

    It will work because when you are using val parameter 'answer' is considered as a property (without val it is considered as parameter) and serialized by Gson using property name as serialized name. At the same time you have the same serialized name @SerializedName("answer") in your base class Answer , therefore there is a conflict while properties are being serialized.