原始类型和数组的 GSON 自定义反序列化器

Posted

技术标签:

【中文标题】原始类型和数组的 GSON 自定义反序列化器【英文标题】:GSON Custom Deserializer for a primitive type and an array 【发布时间】: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,因为该应用有键值对 【参考方案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();
    

希望,它有效。 :)

【讨论】:

以上是关于原始类型和数组的 GSON 自定义反序列化器的主要内容,如果未能解决你的问题,请参考以下文章

JSON 序列化与反序列化(-)泛型 及 java.lang.reflect.Type

当 Json 遇到 Map

如何通过 gson 将 json 反序列化为嵌套的自定义地图?

如何让 System.Text.Json 将对象反序列化为其原始类型?

boost序列化,原始C数组的反序列化

有啥方法可以使用 gson 反序列化 BigQuery Numeric 数据类型