9

I have a JSON response from my server which is dynamic in nature and I cannot map it into a Kotlin Data Class.

I would like to create a org.json.JSONObject out of it and parse it from there. I've looked around SO and Moshi's doc but couldn't find any easy way of achieving this.

Any suggestions?

Jignesh Shah
  • 599
  • 1
  • 5
  • 13
  • what do you mean by "response from my server which is dynamic in nature"? You should know how your server behaves in order to serialize data in your app. Post some server responses – Nicola Gallazzi Nov 27 '19 at 09:57
  • @Nicola - I get a JSONArray containing Strings or Integers or a custom object, based on certain conditions. May not be the best schema I agree, but this is something I'm trying to address. – Jignesh Shah Nov 27 '19 at 10:21
  • I would identify a finite number of response types and for each response I would define a moshi custom adapter – Nicola Gallazzi Nov 27 '19 at 10:42

1 Answers1

11

I've stumbled upon the same problem recently. I needed to resend some of the data from one endpoint to another with adding some new stuff to it.

The response from the server looks like this:

{
    "someProperty": "value",
    "someOtherProperty": "otherValue",
    "someDynamicProperty": {
        // There may be anything including nested structures, not known beforehand
    }
}

Normally Moshi doesn't have built-in support for something like this, but it allows you to build your own adapters and handle the parsing logic.

What you need is define the type that you want to receive as a result:

@JsonClass(generateAdapter = true)
data class CustomResponse(
    val someProperty: String,
    val someOtherProperty: String,
    val someDynamicProperty: JSONObject?
)

Then, you need to create a custom adapter to handle the parsing:

internal object JSONObjectAdapter {

    @FromJson
    fun fromJson(reader: JsonReader): JSONObject? {
        // Here we're expecting the JSON object, it is processed as Map<String, Any> by Moshi
        return (reader.readJsonValue() as? Map<String, Any>)?.let { data ->
            try {
                JSONObject(data)
            } catch (e: JSONException) {
                // Handle error if arises
            }
        }
    }

    @ToJson
    fun toJson(writer: JsonWriter, value: JSONObject?) {
        value?.let { writer.value(Buffer().writeUtf8(value.toString())) }
    }
}

Then, just add this adapter to Moshi builder on creation:

Moshi.Builder().add(JSONObjectAdapter).build()

or use Moshi annotations if you want to apply it only to some particular properties.

Georgiy Shur
  • 748
  • 1
  • 8
  • 19
  • for those needing a more detailed example or are working with parsing complex objects see [this](https://stackoverflow.com/questions/51548692/moshi-cannot-find-my-custom-adapter-written-in-kotlin-for-a-parameterized-type) and [this](https://github.com/loewenfels/dep-graph-releaser/blob/66c822830aa38ac6b4a2278dfe0020d551782bf0/dep-graph-releaser-serialization/src/main/kotlin/ch/loewenfels/depgraph/serialization/PairAdapterFactory.kt) – lasec0203 Sep 18 '20 at 19:36