Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I parse Nested JSON with dynamic keys in Android kotlin, Moshi and Retrofit?

How can I parse Nested JSON with NESTED dynamic keys in Android kotlin, Moshi and Retrofit?

I get this JSON from alpha-vantage.

Format example:

{
    "Meta Data": {
        "1. Information": "Intraday (15min) open, high, low, close prices and volume",
        "2. Symbol": "AAME",
        "3. Last Refreshed": "2019-11-18 16:00:00",
        "4. Interval": "15min",
        "5. Output Size": "Compact",
        "6. Time Zone": "US/Eastern"
     },
    "Time Series (15min)": {//Dynamic - > Time Series (5min) / Time Series (30min)
        "2019-11-18 16:00:00": {//Dynamic
            "1. open": "1.6700",
            "2. high": "1.6700",
            "3. low": "1.5700",
            "4. close": "1.5700",
            "5. volume": "1521"
        },
        "2019-11-18 15:45:00": {//Dynamic
            "1. open": "1.6600",
            "2. high": "1.7400",
            "3. low": "1.6600",
            "4. close": "1.7400",
            "5. volume": "355"
        }
    }
}

I tried to use custom adapter but I can't find a way to parse a double nested dynamic keys with it. For now I use manual parsing:

fun convertJsonToItemDetails(jso: JSONObject) {
    val meta: JSONObject? = jso.optJSONObject("Meta Data")
    var metaData: ItemMetaData? = null
    meta?.apply {
        val information = optString("1. Information")
        val symbol = optString("2. Symbol")
        val lastRefreshed = optString("3. Last Refreshed")
        val interval = optString("4. Interval")
        val outputSize = optString("5. Output Size")
        val timeZone = optString("6. Time Zone")
        metaData =
            ItemMetaData(information, symbol, lastRefreshed, interval, outputSize, timeZone)
    }
    if (metaData == null) {
        //TODO return error object
        return
    }

    val timeSeriesJSON = jso.optJSONObject("Time Series (${metaData?.interval})")
    val timeSeires = HashMap<String, IntervalOutput>()
    if (timeSeriesJSON == null) {
        //TODO return error object
        return
    }
    for (key in timeSeriesJSON.keys()) {
        val intervalOutPutJSON = timeSeriesJSON.getJSONObject(key)
        val open = intervalOutPutJSON.getString("1. open")
        val high = intervalOutPutJSON.getString("2. high")
        val low = intervalOutPutJSON.getString("3. low")
        val close = intervalOutPutJSON.getString("4. close")
        val volume = intervalOutPutJSON.getString("5. volume")
        timeSeires[key] = IntervalOutput(open, high, low, close, volume)
    }
    val itemDetails = ItemDetails(metaData, timeSeires)
    _itemDetails.value = itemDetails
}
like image 831
Maor Hadad Avatar asked Nov 19 '19 14:11

Maor Hadad


People also ask

How does dynamic JSON retrofit handle?

The prefix will change from string to object randomly in both success(...) and failure(...) methods! In above code POJO TrackerRefResponse. java prefix responseMessage is set to string or object of type responseMessage , so we can create the POJO with ref variable with same name (java basics :) )

Which is better GSON or Moshi?

Moshi is way faster than Gson(Link1, ) and uses less memory, This is due to its usage of Okio which can predict or expect ahead of the time the keys which helps on ignoring the unknown or unwanted fields while parsing a stream (A good article on this).

What is Moshi in Android Kotlin?

Moshi is a modern JSON library for Android, Java and Kotlin. It makes it easy to parse JSON into Java and Kotlin classes: Note: The Kotlin examples of this README assume use of either Kotlin code gen or KotlinJsonAdapterFactory for reflection. Plain Java-based reflection is unsupported on Kotlin classes. Java.


1 Answers

Use JSONObject keys() to get the Dynamic Keys and then Iterate each particular key to get to the dynamic value.

A very short to print all the JSONObject of 1st Tier is look like this.

var dynamicJSON: String = {
    "Meta Data": {
        "1. Information": "Intraday (15min) open, high, low, close prices and volume",
        "2. Symbol": "AAME",
        "3. Last Refreshed": "2019-11-18 16:00:00",
        "4. Interval": "15min",
        "5. Output Size": "Compact",
        "6. Time Zone": "US/Eastern"
     },
    "Time Series (15min)": {//Dynamic - > Time Series (5min) / Time Series (30min)
        "2019-11-18 16:00:00": {//Dynamic
            "1. open": "1.6700",
            "2. high": "1.6700",
            "3. low": "1.5700",
            "4. close": "1.5700",
            "5. volume": "1521"
        },
        "2019-11-18 15:45:00": {//Dynamic
            "1. open": "1.6600",
            "2. high": "1.7400",
            "3. low": "1.6600",
            "4. close": "1.7400",
            "5. volume": "355"
        }
    }
}  

Then After you have to start parsing it as JSONObject and then further follows the same process for the JSONObject "Meta Data" & "Time Series (15min)"

val dynamicjson: JSONObject = JSONObject(dynamicJSON)
val keys: Iterator<*> = dynamicjson.keys()

while (keys.hasNext()) { // loop to get the dynamic key
   val currentDynamicKey = keys.next() as String
   // get the value of the dynamic key
   val currentDynamicValue: JSONObject = dynamicjson.getJSONObject(currentDynamicKey)
   // do something here with the value... or either make another while loop to Iterate further

   Log.e("JSON Value", currentDynamicValue.toString())
}

The OUTPUT looks like this when I executed the code:

2019-12-12 15:21:08.399 19798-19798/com.animusabhi.dynamicjsonparsing E/JSON Value: {"1. Information":"Intraday (15min) open, high, low, close prices and volume","2. Symbol":"AAME","3. Last Refreshed":"2019-11-18 16:00:00","4. Interval":"15min","5. Output Size":"Compact","6. Time Zone":"US/Eastern"}

2019-12-12 15:21:09.158 19798-19798/com.animusabhi.dynamicjsonparsing E/JSON Value{"2019-11-18 16:00:00":{"1. open":"1.6700","2. high":"1.6700","3. low":"1.5700","4. close":"1.5700","5. volume":"1521"},"2019-11-18 15:45:00":{"1. open":"1.6600","2. high":"1.7400","3. low":"1.6600","4. close":"1.7400","5. volume":"355"}}

check and let me know if it not works.

like image 98
Abhinav Suman Avatar answered Oct 13 '22 11:10

Abhinav Suman