Moshi Parse Flat Json as Nested Object

October 21, 2018

I would like to parse the following json

{  
   "name":"Hello",
   "media_key":"I am Key",
   "media_blob_url":"I am Url",
   "media_blob_key": null
}

into the following object

class BlobType(
        @Json(name = "media_key") var mediaKey: String? = null,
        @Json(name = "media_blob_url") var mediaBlobUrl: String? = null,
        @Json(name = "media_blob_key") var mediaBlobKey: String? = null
        )

class Item (
        var name: String? = null,
        @Json(name="media_key") var blob: BlobType? = null
)

class AnotherItem (
        var name: String? = null,
        var address: String? = null,
        @Json(name="media_key") var blob: BlobType? = null
)

and convert the object to flat json again.

NOTE: Something like Android Room Embedded where one object could be used to store multiple columns.

I probably need to write an Adapter for this purpose.

The main problem is adapter expect a single field to return a single result only (either a string or an object), but now I need to write a ToJson which convert a single object into 3 json fields and another FromJson to combine 3 json fields into a single object.

Somehow I manage to hack/hardcode ToJson which convert one object into 3 fields.

class BlobTypeJsonAdapter {
        /*
        @FromJson
        fun blogTypeFromJson(reader: JsonReader): BlobType {
            val item = BlobType()
            item.mediaKey = reader.nextString()
            System.out.println("nextName="+reader.nextName())
            return item
        }
         */

        @ToJson
        fun blogTypeToJson(writer: JsonWriter, item: BlobType) {
            writer.value(item.mediaKey)
            writer.name("media_blog_key").value(item.mediaBlobKey)
            writer.name("media_blog_url").value(item.mediaBlobUrl)
        }
}
val moshi = = Moshi.Builder()
        .add(BlobTypeJsonAdapter())
        .add(KotlinJsonAdapterFactory())
        .build()

val adapter = moshi.adapter<Item>(Item::class.java)

val item = Item(name="Hello", blob = BlobType(mediaKey = "I am Key", mediaBlobUrl = "I am Url"))
val jsonString = adapter.toJson(item)

Output

{  
   "name":"Hello",
   "media_key":"I am Key",
   "media_blob_url":"I am Url"
}

I didn’t persuit this idea further because I feel the approach and solution is “unnatural” (merge multiple fields into object) and might introduce future confusion.

NOTE: Technically, I could make those fields private and create a object property with getter/setter to return a object based on these private fields.

NOTE: I could use EventJsonAdapter example, but would need to write an adapter for each class (Item and AnotherItem) and could not reuse the shared portion of BlobType only.

Besides, my moshi-fu is not strong enough and further experimentation will consume more time and introduce more complexity vs the benefit gained.

References:

This work is licensed under a
Creative Commons Attribution-NonCommercial 4.0 International License.