Android Moshi unable to apply custom adapter

380 views Asked by At

My use case is that in data classes wherever I have used Boolean as type, in JSON that can either comes as double quote string ("true") or normal boolean (true)
Earlier I was using Gson & it used to automatically handle all these cases but when trying to migrate to moshi I am facing multiple issues. I even wrote custom adapter for it

class BooleanAdapter : JsonAdapter<Boolean>() {
    @FromJson
    override fun fromJson(reader: JsonReader): Boolean {
        return when (reader.peek()) {
            JsonReader.Token.BOOLEAN -> reader.nextBoolean()
            JsonReader.Token.STRING -> {
                when (val value = reader.nextString()) {
                    "true", "True", "TRUE", "1" -> true
                    "false", "False", "FALSE", "0" -> false
                    else -> throw JsonDataException("Invalid boolean value: $value")
                }
            }
            JsonReader.Token.NUMBER -> {
                when (val value = reader.nextInt()) {
                    0 -> false
                    1 -> true
                    else -> throw JsonDataException("Invalid boolean value: $value")
                }
            }
            else -> throw JsonDataException("Expected boolean or string or int value but was ${reader.peek()}")
        }
    }

    @ToJson
    override fun toJson(writer: JsonWriter, value: Boolean?) {
        writer.value(value)
    }
}

Then used this to inject Moshi in retrofit

@Singleton
@Provides
@MoshiBuilder
fun providesDeserializer(): Moshi =
    Moshi.Builder()
        .add(BooleanAdapter())
        .build()

And here is a sample data class

@JsonClass(generateAdapter = true)
data class ItemResponse(

    @field:JsonAdapter(BooleanAdapter::class)
    @Json(name = "mSet") val IsSet: Boolean,

    @Json(name = "name") val name: String,

    @field:JsonAdapter(BooleanAdapter::class)
    @Json(name = "isPrimary") val isPrimary: Boolean
)

Getting the following error

com.squareup.moshi.JsonDataException: Expected a boolean but was STRING at path

In JSON this parameter is coming as "true"
What am I missing here. It seems I made a bad choice to migrate to Moshi

1

There are 1 answers

0
Eric Cochran On

Remove the JsonAdapter inheritance. It's unnecessary with the ToJson and FromJson annotations and is actually causing the subtle problem with Kotlin and boxed primitive types.

By extending JsonAdapter, you are creating an adapter for the boxed type Boolean, not the primitive type boolean, but your ItemResponse declares primitve boolean types, so Moshi looks up the adapter for the primitive type, not the boxed type that you have your adapter for.

To get the result you want, simply ensure your adapter is for the primitive boolean type.

class BooleanAdapter {
  @FromJson
  fun fromJson(reader: JsonReader): Boolean {
    return when (reader.peek()) {
      JsonReader.Token.BOOLEAN -> reader.nextBoolean()
      JsonReader.Token.STRING -> {
        when (val value = reader.nextString()) {
          "true", "True", "TRUE", "1" -> true
          "false", "False", "FALSE", "0" -> false
          else -> throw JsonDataException("Invalid boolean value: $value")
        }
      }
      JsonReader.Token.NUMBER -> {
        when (val value = reader.nextInt()) {
          0 -> false
          1 -> true
          else -> throw JsonDataException("Invalid boolean value: $value")
        }
      }
      else -> throw JsonDataException("Expected boolean or string or int value but was ${reader.peek()}")
    }
  }

  @ToJson
  fun toJson(writer: JsonWriter, value: Boolean?) {
    writer.value(value)
  }
}
Moshi.Builder().add(BooleanAdapter()).build()