Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to parse a json list of multiple type in Moshi

Got a json list mixed with plain String and Image object like this:

 {
  "mixList": [
    "string",
    {
      "imageUrl": "http://...",
      "height": 320,
      "width": 480
    }
  ]
}

How to parse with Moshi?

I would expect to have a List<Data>, where StringData extends Data and ImageData extends Data

like image 859
Felix.D Avatar asked Oct 19 '16 04:10

Felix.D


1 Answers

I've solved this problem using Moshi with a CustomAdaptor. After a lot of research I wasn't able to find a better "out-of-the-box" solution. This solutions is in kotlin but can easily be ported to Java as well.

First let's define the types we are trying to parse here. I'll call the wrapper type, that contains the mixList, Response:

@JsonClass(generateAdapter = true)
data class Response(val mix_types: Data)

And the 2 different types that can be inside the list StringData and ImageData:

sealed class Data {
    data class StringData(val value: String) : Data()

    @JsonClass(generateAdapter = true)
    data class ImageData(
        val imageUrl: String,
        val height: Int,
        val width: Int
    ) : Data()
}

As I'm using Moshi code-gen, so I have annotated Response and ImageData with @JsonClass(generateAdapter = true) so that Moshi will generate the adapters for these types(and I'll leverage this in my custom adapter).

I want to provide my own custom adapter to the Data type, and I won't also want the adapter Moshi would generate for the StringData type, since this is precisely what I want to serialise/deserialise into a String, so I won't annotate these classes.

Now I'm gonna write my custom adapter like this:

class DataCustomAdapter {
    @FromJson
    fun fromJson(jsonReader: JsonReader, delegate: JsonAdapter<ImageData>): Data? {
        return if (jsonReader.peek() == BEGIN_OBJECT) {
            delegate.fromJson(jsonReader)
        } else {
            StringData(jsonReader.nextString())
        }
    }

    @ToJson
    fun toJson(jsonWriter: JsonWriter, data: Data, delegate: JsonAdapter<ImageData>) {
        when (data) {
            is ImageData -> delegate.toJson(jsonWriter, data)
            is StringData -> jsonWriter.value(data.value)
        }
    }
}

all it's missing now is to register the custom adapter with Moshi:

private val moshi = Moshi.Builder()
    .add(DataCustomAdapter())
    .build()
like image 149
Thiago Saraiva Avatar answered Sep 24 '22 07:09

Thiago Saraiva