【问题标题】:How can I parse Nested JSON with dynamic keys in Android kotlin, Moshi and Retrofit?如何在 Android kotlin、Moshi 和 Retrofit 中使用动态键解析嵌套 JSON?
【发布时间】:2019-11-19 14:50:37
【问题描述】:

如何在 Android kotlin、Moshi 和 Retrofit 中使用 NESTED 动态键解析嵌套 JSON?

我从alpha-vantage 得到这个 JSON。

格式示例:

{
    "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"
        }
    }
}

我尝试使用自定义适配器,但找不到用它解析双嵌套动态键的方法。 现在我使用手动解析:

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
}

【问题讨论】:

  • 由于 JSON 是当今数据交换的新标准,难道没有可用的 JSON 解析库吗?
  • 是的,你是对的 JSON 是新标准,但我没有找到用 Moshi 解析这个用例的方法。@Mikkel
  • android kotlin 是否有“for-each”类型的组件?伪代码:对 JSONObject JO 中的每个键 k 做 X
  • 我不想手动解析它(我现在做了什么),我想用 Moshi 来做。
  • 我花了一些时间试图为您提出一个解决方案,但是您的 JSON 在键名中包含空格,最终破坏了 Moshi 的 Kotlin Codegen。我已经为此提交了一个问题 (github.com/square/moshi/issues/1052) 并决定暂时离开这个问题,因为我没有时间给你写一个适配器工厂。

标签: android kotlin retrofit2 kotlin-android-extensions moshi


【解决方案1】:

使用JSONObject keys() 获取Dynamic Keys,然后使用Iterate 每个特定键获取动态值。

打印所有 1st Tier 的 JSONObject 非常短,如下所示。

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"
        }
    }
}  

然后,您必须开始将其解析为 JSONObject,然后进一步对 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())
}

当我执行代码时,输​​出看起来像这样:

2019-12-12 15:21:08.399 19798-19798/com.animusabhi.dynamicjsonparsing E/JSON 值:{“1.信息”:“盘中(15 分钟)开盘价、最高价、最低价、收盘价和音量","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 值{"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.开盘":"1.6600","2.高":"1.7400","3.低":"1.6600","4.收盘":"1.7400"," 5. 音量":"355"}}

检查一下,如果它不起作用,请告诉我。

【讨论】:

    【解决方案2】:

    只要将你的 json 字符串传递给这个方法,它肯定会工作。

    private void parseJson(String jsonString) {
        try {
            //  pass your response json string here
            JSONObject object = new JSONObject(jsonString);
    
            Iterator<String> it = object.keys();
            while (it.hasNext()) {
                String key = it.next();
                try {
                    if (object.get(key) instanceof JSONObject) {
                        Log.e("Main OBJ", key);
                        JSONObject object2 = object.getJSONObject(key);
                        Iterator<String> it2 = object2.keys();
                        while (it2.hasNext()) {
                            String key2 = it2.next();
                            if (object2.get(key2) instanceof JSONObject) {
                                Log.e("Sub OBJ", key2);
                                JSONObject object3 = object2.getJSONObject(key2);
                                Iterator<String> it3 = object3.keys();
                                while (it3.hasNext()) {
                                    String key4 = it3.next();
                                    Log.e("Values", key4);
                                }
                            } else {
                                Log.e("Values", object2.getString(key2));
                            }
                        }
    
                    } else {
                        System.out.println(key + ":" + object.getString(key));
                    }
                } catch (Throwable e) {
                    try {
                        System.out.println(key + ":" + object.getString(key));
                    } catch (Exception ee) {
                        ee.printStackTrace();
                    }
                    e.printStackTrace();
                }
            }
    
        } catch (JSONException e) {
            e.printStackTrace();
        }
    }
    

    输出:

    Main OBJ: Meta Data
    Values: Intraday (15min) open, high, low, close prices and volume
    Values: AAME
    Values: 2019-11-18 16:00:00
    Values: 15min
    Values: Compact
    Values: US/Eastern
    Main OBJ: Time Series (15min)
    Sub OBJ: 2019-11-18 16:00:00
    Values: 1. open
    Values: 2. high
    Values: 3. low
    Values: 4. close
    Values: 5. volume
    Sub OBJ: 2019-11-18 15:45:00
    Values: 1. open
    Values: 2. high
    Values: 3. low
    Values: 4. close
    Values: 5. volume
    

    【讨论】:

    • 它没有回答我的问题。它默认选择导致赏金期结束。
    • 这不是正确的答案吗?我认为您不能使用 json 解析库,因为您需要先指定所有键,如果任何键更改它们会直接引发错误响应,并且您的响应键是动态的。
    • 这不是正确的答案。您可以使用 Map 解析动态键。这里的问题是嵌套的动态键。另外,就像这里的其他人说的那样,其他库也可以(我没有检查)。
    猜你喜欢
    • 2021-02-03
    • 2021-04-24
    • 2018-05-31
    • 2015-04-07
    • 2016-02-14
    • 2019-09-25
    • 1970-01-01
    • 2020-04-15
    • 2018-07-12
    相关资源
    最近更新 更多