c_idle
c_idle

Reputation: 1574

Moshi: PolymorphicJsonAdapterFactory is it possible to get the type in withDefaultValue?

I have a moshi PolymorphicJsonAdapterFactory and it works great.

.withSubtype(ColdWeather::class.java, "Cold")
.withSubtype(HotWeather::class.java, "Hot")
.withDefaultValue(//how to grab the label)

The method withDefaultValue is a great catch all, but my BE team wants me to log the actual label that comes down in order to help catch a bug that's going on on their end. As far as I can tell... in the withDefaultValue I can't grab a reference to the label which in this case the backend is sending back "Medium".

I feel like there must be a way to grab this label (but I'm missing something simple?) so I can log it and possibly propagate it in the withDefaultValue method.

Upvotes: 3

Views: 1864

Answers (1)

Dat Pham Tat
Dat Pham Tat

Reputation: 1463

I stumbled on the issue a while ago. I found it impossible to achieve with just using .withDefaultValue method. So far I did not find better solution other than .withFallbackJsonAdapter (I am using moshi version 1.12), which lets you parse the json manually in case the label is unknown to your PolymorphicJsonAdapterFactory adapter. The documentation says:

/**
   * Returns a new factory that with default to {@code fallbackJsonAdapter.fromJson(reader)} upon
   * decoding of unrecognized labels.
   *
   * <p>The {@link JsonReader} instance will not be automatically consumed, so make sure to consume
   * it within your implementation of {@link JsonAdapter#fromJson(JsonReader)}
   */
  public PolymorphicJsonAdapterFactory<T> withFallbackJsonAdapter(
      @Nullable JsonAdapter<Object> fallbackJsonAdapter) {
    return ...
  }

I assume your code is somewhat like this (simplified):

    interface Weather {
        val type: String
    }

    @JsonClass(generateAdapter = true)
    class ColdWeather( @Json(name = "type") override val type: String) : Weather

    @JsonClass(generateAdapter = true)
    class HotWeather( @Json(name = "type") override val type: String) : Weather

    val weatherAdapter = PolymorphicJsonAdapterFactory.of(Weather::class.java, "type")
        .withSubtype(ColdWeather::class.java, "Cold")
        .withSubtype(HotWeather::class.java, "Hot")

and you receive a json similar to this:

{ "weather" : { "type" : "Cold" } }

To receive an unknown label, I would do something like this:

class UnknownWeather(override val type: String) : Weather

val weatherAdapter = PolymorphicJsonAdapterFactory.of(Weather::class.java, "type")
    .withSubtype(ColdWeather::class.java, "Cold")
    .withSubtype(HotWeather::class.java, "Hot")
    .withFallbackJsonAdapter((object : JsonAdapter<Any>() {
        override fun fromJson(reader: JsonReader): UnknownWeather {
            var type = ... // parse it from the reader
            return UnknownWeather(type)
        }

        override fun toJson(writer: JsonWriter, value: Any?) {
            // nothing to do
        }
    }))

Of course that means that you will have to dig a bit into JsonReader, but it has a fairly understandable interface, you basically iterate through the properties of the json object and extract what you need, in our case just the "type" property.

FYI, seems like more people had problem with this: https://github.com/square/moshi/issues/784

Upvotes: 2

Related Questions