34

I'm trying to parse following dynamic key Json String.

"report":{
    "data":{
        "results":{
            "558952cca6d73d7d81c2eb9d":{
                "Max":-1,
                "Min":-1,
                "Slope":-1,
            },

            "558ce148a6d73d7d81c2fa8a":{
                "Max":-2,
                "Min":-1,
                "Slope":-2,
            }
        }
    }
}

Following I'm trying to get data, but getting error while parse last dynamic json String.

 public class Report {
        @SerializedName("data")
        @Expose
        private Data data;

        public Data getData() {
            return data;
        }

        public void setData(Data data) {
            this.data = data;
        }

        @Override
        public String toString() {
            return "Report{" +
                    "data=" + data +
                    '}';
        }
    }

    public class Data {
        @SerializedName("results")
        @Expose
        private ResultInside result;

        public ResultInside getResult() {
            return result;
        }

        public void setResult(ResultInside result) {
            this.result = result;
        }
    }

    public class ResultInside {
        /*@SerializedName("results")
        @Expose*/
        private Map<String, Vitals> elemDetails = new HashMap<>();

        public Map<String, Vitals> getElemDetails() {
            return elemDetails;
        }

        public void setElemDetails(Map<String, Vitals> elemDetails) {
            this.elemDetails = elemDetails;
        }
    }

Any suggestion how to parse in this case !

CoDe
  • 11,056
  • 14
  • 90
  • 197
  • @Shubh Hai, I am having the same issue, I don't know how to add Pojo class for Dynamic Key, Can you tell what you have used in Vitals Class? – MathankumarK Apr 19 '16 at 11:59
  • @MathaN check with json string and respected Data class. Report -> Data -> results -> Vitals. And so vital contain max, min, slope as variable. I did mistake to Create another ResulInside class inside Result with thought, it will map **runtime Key** with **Vitals** class but that is not the case. Check accepted answer below, instead to create **ResultInsid** handle it **Result** class itself. – CoDe Apr 19 '16 at 14:12

2 Answers2

67

Your resultInside class is adding an extra object layer that does not exist in your JSON. Try moving the map to your Data class results field.

public class Data {
    @SerializedName("results")
    @Expose
    private Map<String, Vitals> result;

    //....
}
iagreen
  • 31,470
  • 8
  • 76
  • 90
  • 1
    In retrofit if you want to parse JSON with dynamic keys (with dynamic names) you need to use HashMap for sure. So this answer is the correct one +1 from me. Just make sure you are putting you HashMap on object level which has static name (not dynamic name) – Stoycho Andreev May 31 '16 at 10:12
  • Thank you so much. You saved my times :) – aligur Jun 28 '16 at 13:05
  • 1
    How to handle(receive data from) the situation if one of children of each dynamic key is JSONArray of JSONObjects. Thanks – shehzy Jul 18 '17 at 07:26
  • 1
    Can you explain why this works? It looks like GSON is skipping the dynamically named key but I don't understand why. –  Aug 04 '18 at 23:13
12

A better way to do this, would be:

----------------------

public class Report {
        @SerializedName("data")
        @Expose
        private Data data;

----------------------

public class Data {


    public HashMap<String, DataValues> dataValues;


    public Data() {
        this.dataVaues = new HashMap<>();
    }
}

-----------------------------

then, create a Parser class like this:

public class DataParser implements JsonDeserializer<Data> {


    @Override
    public Data deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {

        Data result = new Data();


        try {
            final HashMap<String, DataValues> map = readServiceUrlMap(json.getAsJsonObject());

            if(map != null) {
                result.dataValues = map;
            }

        }catch (JsonSyntaxException ex){
            return null;
        }

        return result;
    }


    private HashMap<String, DataValues> readServiceUrlMap(final JsonObject jsonObject) throws JsonSyntaxException {

        if(jsonObject == null) {
            return null;
        }
        Gson gson = new Gson();

        HashMap<String, DataValues> products = new HashMap<>();

        for (Map.Entry<String, JsonElement> entry : jsonObject.entrySet()) {

            String key = entry.getKey();
            DataValues value = gson.fromJson(entry.getValue(), DataValues.class);
            products.put(key, value);
        }
        return products;
    }


----------------------------------------------

after that, type this in your ApiClient class

public class ApiClient {


    private static Retrofit retrofit = null;

    public static Retrofit getClient(String baseUrl) {

        if(retrofit == null) {

            GsonBuilder gsonBuilder = new GsonBuilder();
            gsonBuilder.registerTypeAdapter(Data.class, new DataParser());

            retrofit = new Retrofit.Builder()
                    .baseUrl(baseUrl)
                    .addConverterFactory(GsonConverterFactory.create(gsonBuilder.create()))
                    .build();

I hope this will help someone

KenHBS
  • 6,756
  • 6
  • 37
  • 52
Ruben Caster
  • 290
  • 1
  • 5
  • 15