Gson - Same field name, different types
Asked Answered
E

2

34

I asked this in a different question today but I'm afraid that won't get any solution because of how it was phrased.

I have a json input that has the following data:

Json

As you can see, the option_value item is an Array in one object and a simple string in another object.

How can I make Gson handle this properly? My class has this described as a List object, so it works for the first few items where option_value is an array, but when it becomes a string, the app crashes and I get a json parse exception.

Is there a workaround for this?

UPDATE

Adding the relevant part of my class as requested:

public class Options
    {
        String product_option_id;
        String option_id;
        String name;
        String type;
        String required;
        List<OptionValue> option_value;

        // get set stuff here

        public class OptionValue
        {
            String product_option_value_id;
            String option_value_id;
            String name;
            String image;
            String price;
            String price_prefix;

            // get set stuff here
        }
    }
Exhibit answered 4/2, 2015 at 10:50 Comment(6)
Hi Asim, can you show your Json Parser ?Drove
Added the relevant part. I'm using Gson so the parsing is done by that library using the Gson.fromJson(string, class) function.Exhibit
You can not achieve this using GSON.fromJson(String json, Class<T> classOfT), you will have to define your adapter... lets say StrangeJsonAdapter by extending com.google.gson.TypeAdapter class... then use StrangeJsonAdapter.fromJson(String json) method to decode your json.Nagey
It was working fine before I started parsing this part of the json (option_value). What makes you think the above json can't be parsed using fromJson?Exhibit
Please, mark Denis answer as answer :)Mackay
Done. Although, I think I used some other method to deal with it. But his is also a very good (and correct) answer.Exhibit
K
32

I have a solution for you :) For this purpose, we should use a custom deserializer. Remake your class like this:

    public class Options{

        @SerializedName ("product_option_id");
        String mProductOptionId;

        @SerializedName ("option_id");
        String mOptionId;

        @SerializedName ("name");
        String mName;

        @SerializedName ("type");
        String mType;

        @SerializedName ("required");
        String mRequired;

        //don't assign any serialized name, this field will be parsed manually
        List<OptionValue> mOptionValue;

        //setter
        public void setOptionValues(List<OptionValue> optionValues){
             mOptionValue = optionValues;
        }

        // get set stuff here
        public class OptionValue
        {
            String product_option_value_id;
            String option_value_id;
            String name;
            String image;
            String price;
            String price_prefix;

            // get set stuff here
        }

    public static class OptionsDeserializer implements JsonDeserializer<Options> {

        @Override
        public Offer deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
            Options options = new Gson().fromJson(json, Options.class);
            JsonObject jsonObject = json.getAsJsonObject();

            if (jsonObject.has("option_value")) {
                JsonElement elem = jsonObject.get("option_value");
                if (elem != null && !elem.isJsonNull()) {  
                     String valuesString = elem.getAsString();
                     if (!TextUtils.isEmpty(valuesString)){
                         List<OptionValue> values = new Gson().fromJson(valuesString, new TypeToken<ArrayList<OptionValue>>() {}.getType());
                         options.setOptionValues(values);
                     }
                }
            }
            return options ;
        }
    }
    }

Before we can let Gson parse json, we should register our custom deserializer:

    Gson gson = new GsonBuilder()              
                .registerTypeAdapter(Options.class, new Options.OptionsDeserilizer())               
                .create();

And now - just call:

    Options options = gson.fromJson(json, Options.class);
Koran answered 4/2, 2015 at 15:28 Comment(3)
I got a StackOverflowError due to the recursive deserialization call on itself at Options options = new Gson().fromJson(json, Options.class); and two new Gson instances are created in the deserialization of a single object. Look out.Esophagitis
@Esophagitis is there a way to prevent this StackOverflow?Headwind
@Headwind I'm not sure. It's been 3.5 years and I don't remember this question, nor my comment. I don't remember what exactly I ran into and when.Esophagitis
P
6

In my situation, the field with same name is "data":{} or "data":[array_with_real_data]. So the code from accepted answer need to be modified slightly, like this:

@Override
public MyClass deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
        throws JsonParseException {
    MyClass bean = new Gson().fromJson(json, MyClass.class);
    JsonObject jsonObject = json.getAsJsonObject();

    if (jsonObject.has("data")) {
        JsonArray array = jsonObject.getAsJsonArray("data");
        if (array != null && !array.isJsonNull()) {
            List<Data> data = new Gson().fromJson(array, new TypeToken<ArrayList<Data>>() {}.getType());
            bean.realData = data;
        }
    }
    return bean ;
}

hope that helps.

Pepperandsalt answered 16/8, 2017 at 20:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.