【问题标题】:GSON Custom Deserializer for a primitive type and an array原始类型和数组的 GSON 自定义反序列化器
【发布时间】:2021-11-01 18:21:49
【问题描述】:

我有一个可能具有不同值的 JSON 响应。

"values": [
                    {
                        "type": "AAA",
                        "value": 3
                    },
                    {
                        "type": "BBB",
                        "value": [
                            {
                                "ABC": 8
                            },
                            {
                                "DEF": 9
                            },
                            {
                                "GHI": 9
                            },
                            {
                                "JKL": 8
                            },
                            {
                                "MNO": 9
                            },
                            {
                                "PQR": 9
                            }
                        ]
                    }
                ]

数据类

data class Values(
    @SerializedName("type")
    @Expose
    val type: String,
    @SerializedName("value")
    @Expose
    val value: Int)

如您所见,value 可以是 Int 或另一个 Json 数组。任何想法如何为此编写反序列化器? Android 应用程序当前正在使用 Int 字段(类型 AAA),我正在尝试集成潜在的数组值(类型 BBB)。谢谢。

【问题讨论】:

  • 即使 value 是单个 int 值,您能否将其设为 Json 数组?我的意思是只需将该 int 值添加到 json 数组并返回它,因为这将使 json 数组在所有情况下都成为统一类型,并且更容易建模,因此更容易反序列化。如果发生这种情况,我有一个解决方案,即如果您可以控制来自后端的数据
  • 我也在研究一些相关的问题,有些场景只是将其转换为原始类型数组。不幸的是,我无法控制数据的嵌套
  • 如果 json 对象的键和值对您都很重要,并且在输入单个值的情况下,您可以做的一件事是创建一个 List<Pair>如果你不会使用它。如果您同意在下面的答案部分中继续使用此方法,我也可以在这种情况下为您提供详细的解决方案,但只需先与您确认
  • 就像下面的一些答案建议的那样,我们将进行自定义反序列化,然后将您的模型 value 更改为 List<Pair 而不是 int 我已经在上面描述了如何处理单个值案例
  • 很遗憾,我无法转换为 List,因为该应用有键值对

标签: android json kotlin gson retrofit


【解决方案1】:

例如,您可以拥有一个带有两个子类的密封类Value

sealed class Value {
    /* type AAA */
    data class IntValue(val value: Int) : Value()
    /* type BBB */
    data class IntPairValue(val value: List<Pair<String, Int>>) : Value() 
}

并编写自定义反序列化器:

class ValueTypeDeserializer : JsonDeserializer<Value?> {
    override fun deserialize(
        json: JsonElement,
        typeOfT: Type,
        context: JsonDeserializationContext
    ): Value? {
        return when {
            json.isJsonPrimitive -> {
                Value.IntValue(json.asJsonPrimitive.asInt)
            }
            json.isJsonArray -> {
                val list = context.deserialize<List<Map<String, Int>>>(json, LIST_OF_MAP_TYPE)
                val pairs = list.map { e -> e.firstNotNullOf { (key, value) -> Pair(key, value) } }
                Value.IntPairValue(pairs)
            }
            else -> null
        }
    }

    companion object {
        private val LIST_OF_MAP_TYPE = object : TypeToken<List<Map<String, Int>>>() {}.type
    }
}

现在您可以使用 @JsonAdapter 注释将这个反序列化器应用到 "value" 字段,例如:

data class Values(
    @SerializedName("type")
    @Expose
    val type: String,
    @SerializedName("value")
    @Expose
    @JsonAdapter(ValueTypeDeserializer::class)
    val value: Value
) {
    val optIntValue: Int?
        get() = (value as? Value.IntValue)?.value
    val optIntPairValue: List<Pair<String, Int>>?
        get() = (value as? Value.IntPairValue)?.value
}

使用optIntValueoptIntPairValue 访问您感兴趣的值。

【讨论】:

    【解决方案2】:

    试试这个代码:

            try {
                JSONObject jsonObject = new JSONObject(); // this is your values object
                Object o = jsonObject.get("value");
                if (o instanceof JSONArray) {
                    //this is jsonarray
                } else if (o instanceof JSONObject) {
                    //this is jsonobject
                } else if (o instanceof Integer) {
                    //this is int
                } else if (o instanceof String) {
                    //this is String
                }
            } catch (JSONException e) {
                e.printStackTrace();
            }
    

    【讨论】:

    • 嗨@Praful Patel,我目前正在使用改造来映射JSON响应,我没有手动映射它们
    • 然后将 value_object 类型设置为“Object”到您的改造模型类中。然后你将我的 if 条件用于你的活动或 getter 方法。
    • 正如我所说,Int 值已经存在使用,我不能只是更改它,因为它会影响很多东西
    【解决方案3】:

    首先,在您的情况下使用手动解析而不是自动解析,因为自动解析可能会出现问题并且在这种情况下也有点复杂。现在您需要将请求更改为“JsonObject”。

    试试这个代码...

    try {
            JSONObject your_response=new JSONObject();
    
            JSONArray jsonArrayMain=your_response.getJSONArray("values");
    
            for (int i = 0; i < jsonArrayMain.length(); i++) {
                JSONObject jsonObject=jsonArrayMain.getJSONObject(i);
                String type=jsonObject.getString("type");
    
                Object o = jsonObject.get("value");
                if (o instanceof JSONArray) {
                    //value is array
                    JSONArray jsonArrayValue=jsonObject.getJSONArray("value");
    
                    for (int j = 0; j <jsonArrayValue.length(); j++) {
    
                        JSONObject subJsonObject=jsonArrayValue.getJSONObject(j);
                        
                        Iterator<String> iterator = subJsonObject.keys();
                        while (iterator.hasNext()) {
                            String key = iterator.next();
                            Log.i("====TAG","key:"+key +"--Value::"+subJsonObject.optString(key);
                        }
                    }
    
                } else if (o instanceof String) {
                    //value is string
                    String value=jsonObject.getString("value");
                }
            }
    
        }catch (Exception e){
            e.printStackTrace();
        }
    

    希望,它有效。 :)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-07-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-01-04
      相关资源
      最近更新 更多