简体   繁体   English

使用 Gson 动态解析 JSON 字段类型

[英]Resolve JSON field types dynamically using Gson

I have the following API response:我有以下 API 响应:

{  
   "data":{  
      "categoryFields":[  
         {  
            "name":"brand",
            "label":"Marca", 
            "values":[  
               {  
                  "key":53,
                  "value":"Alfa Romeo"
               },
               {  
                  "key":55,
                  "value":"Audi"
               }
            ]
         },
         {  
            "name":"year",
            "label":"Año", ,
            "dataType":"select",
            "values":[  
               {  
                  "key":2017,
                  "value":2017
               },
               {  
                  "key":2016,
                  "value":2016
               }
            ]
         },

      ]
   }
}

Ok, in the first categoryField the values are:好的,在第一个categoryField ,值是:

"key":53 INT,
"value":"Alfa Romeo", STRING

In the second categoryField the values are:在第二个categoryField ,值是:

 "key":2017, INT
 "value":2017, INT

Also there's another type:还有另一种类型:

 "key":"string", STRING
 "value":"String", STRING

I need a class that can handle those types of data.我需要一个可以处理这些类型数据的类。 Something like:就像是:

public class Value {

    @SerializedName("key")
    @Expose
    private DYNAMIC_TYPE key;

    @SerializedName("value")
    @Expose
    private DYNAMIC_TYPE value; 

}

How can I do that?我怎样才能做到这一点? Or there's a Gson function to help me with that?或者有一个 Gson 函数可以帮助我解决这个问题?

solution解决方案

public class CategoryValueDeserializer implements JsonDeserializer<CategoryValue> {
    @Override
    public CategoryValue deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {

        final JsonObject jsonObject = json.getAsJsonObject();

        final JsonElement jsonKey = jsonObject.get("key");
        final String key = jsonKey.getAsString();

        final JsonElement jsonValue = jsonObject.get("value");
        final String value = jsonValue.getAsString();

        CategoryValue categoryValue = new CategoryValue();
        categoryValue.setKey(key);
        categoryValue.setValue(value);

        return categoryValue;

    }
}

//retrofit //改造

 final Gson gson = new GsonBuilder()
                .registerTypeAdapter(Response.class, new CategoryValueDeserializer())
                .create();

        Retrofit.Builder builder = new Retrofit.Builder()
                .baseUrl(END_POINT)
                .addConverterFactory(GsonConverterFactory.create(gson))
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create());

Here is a working solution这是一个有效的解决方案

First create a POJO class like below首先创建一个像下面这样的 POJO 类

import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;

public class KeyValuePair {
    @SerializedName("key")
    @Expose
    private String key;
    @SerializedName("value")
    @Expose
    private Object value;

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }

    public Object getValue() {
        return value;
    }

    public void setValue(Object value) {
        this.value = value;
    }
}

Then for Gson to serialize this class properly use this code然后对于 Gson 正确序列化此类使用此代码

import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;

import java.lang.reflect.Type;

public class KeyValuePairDeserializer implements JsonDeserializer<KeyValuePair> {
    @Override
    public KeyValuePair deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {

        final JsonObject jsonObject = json.getAsJsonObject();

        final JsonElement jsonKey = jsonObject.get("key");
        final String key = jsonKey.getAsString();

        final JsonElement jsonValue = jsonObject.get("value");
        Object value = jsonValue.getAsString();
        if (jsonValue.isJsonPrimitive()) {
            JsonPrimitive jsonPrimitive = jsonValue.getAsJsonPrimitive();
            if (jsonPrimitive.isBoolean())
                value = jsonValue.getAsBoolean();
            else if (jsonPrimitive.isString())
                value = jsonValue.getAsString();
            else if (jsonPrimitive.isNumber()){
                value = jsonValue.getAsNumber();
            }
        }
        KeyValuePair categoryValue = new KeyValuePair();
        categoryValue.setKey(key);
        categoryValue.setValue(value);
        return categoryValue;
    }
}

Register the adapter with Gson like this像这样用 Gson 注册适配器

 Gson provideGson() {
        return new GsonBuilder()
                .registerTypeAdapter(KeyValuePair.class, new KeyValuePairDeserializer())
                .create();
    }

Now you access the values by using getter methods like these现在您可以使用这些 getter 方法访问这些值

public String getString(String key) {
    return (String) value;
}

public int getInt(String key) {
    return ((Number) value).intValue();
}

public long getLong(String key) {
    return ((Number) value).longValue();
}

public float getFloat(String key) {
    return ((Number) value).floatValue();
}

public boolean getBoolean(String key) {
    return (boolean) value;
}

I always had something like dataType provided by BE.我总是有类似 BE 提供的dataType东西。 Then I could base on its value during deserialization.然后我可以在反序列化过程中基于它的值。 You've got dataType too, but only for Int, Int case.您也有dataType ,但仅适用于Int, Int大小写。

Well, I'd do one of two things:好吧,我会做两件事之一:

  • Talk with BE to send different dataType for every different case.与 BE 交谈以针对每种不同的情况发送不同的dataType类型。 This is good for types with many parameters and only few different dataTypes.这对于具有许多参数和只有少数不同数据类型的类型很有用。 Then I deserialize with JsonDeserializer to what I need, conditionally - based on dataType .然后我用JsonDeserializer反序列JsonDeserializer到我需要的,有条件地 - 基于dataType
  • If you have only two values and three possible cases, it's easy to check via isString and others, as proposed in different answer.如果您只有两个值和三个可能的情况,则很容易通过isString和其他人进行检查,如不同答案中所建议的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM