0

I have the JSON below:

{
    "abc": {
        "linkedTo": "count",
        // possibly more data...
    },
    "plmtq": {
        "linkedTo": "title",
        "decode": "true",
        // possibly more data...
    }
}

I need to load this JSON into a Map<String, Holder>, with keys "abc" and "plmtq".
Below is my Holder class:

public class Holder {
  private final Map<String, String> data;

  public Holder(Map<String, String> data) {
    this.data = data;
  }

  // More methods in this class...
}

The JSON does not match my class structure, but I cannot change the JSON or the classes.
What I need is a way to customize the deserialization so I can parse the JSON into a Map<String, Holder>.
Is there any way to do this?


Below is the code which does that, but it looks too complex and there must be a simpler way to do it...

private static Map<String, Holder> getMap(String jsonLine)
{
    Map<String, Holder> holder = new HashMap<>();
    JsonElement jelement = new JsonParser().parse(jsonLine);
    JsonObject jobject = jelement.getAsJsonObject();

    for (Map.Entry<String, JsonElement> entry : jobject.entrySet())
    {
        Map<String, String> metadata = new HashMap<>();
        JsonObject element = entry.getValue().getAsJsonObject();

        for (Map.Entry<String, JsonElement> entry2 : element.entrySet())
        {
            metadata.put(entry2.getKey(), entry2.getValue().getAsString());
        }

        holder.put(entry.getKey(), new Holder(metadata));
    }
    return holder;
}
MikO
  • 18,243
  • 12
  • 77
  • 109
john
  • 11,311
  • 40
  • 131
  • 251
  • That JSON doesn't fit your `Holder` object hierarchy. – Sotirios Delimanolis Dec 08 '16 at 00:09
  • @SotiriosDelimanolis It does fit right? `holder` `(Map holder = new HashMap<>();)` map will have key as `abc` and its value will be `Holder` class in which data map key has `linkedTo` and value is `count`. Similarly for `plmtq`. – john Dec 08 '16 at 00:12
  • Sorry, your parsing method is confusing. This might be doable in a single line with Jackson. Gson doesn't have that parsing many features. – Sotirios Delimanolis Dec 08 '16 at 00:23
  • Yeah my parsing method is very bad that's why I wanted to see whether there is any better way to do that? – john Dec 08 '16 at 00:47

1 Answers1

3

If you really need to keep your Holder class as is, the only option I can think of is to create a custom deserializer for your Holder class.

By doing this, whenever you tell Gson to parse some JSON into a Holder object, it will use your deserializer, instead of doing it 'the normal way' (i.e., mapping JSON keys to object properties).

Something like this:

private class HolderDeserializer implements JsonDeserializer<Holder> {

  @Override
  public Holder deserialize(JsonElement json, Type type, JsonDeserializationContext context) 
    throws JsonParseException {

    Type mapType = new TypeToken<Map<String, String>>() {}.getType();
    Map<String, String> data = context.deserialize(json, mapType);

    return new Holder(data);
  }  
}

Then you register your deserializer when creating the Gson object:

GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(Holder.class, new HolderDeserializer());
Gson gson = gsonBuilder.create();

And finally, you parse your JSON like this:

Type responseType = new TypeToken<Map<String, Holder>>() {}.getType();
Map<String, Holder> response = gson.fromJson(jsonLine, responseType);

You may want to look at Gson's docs for custom (de)serialization and this other answer of mine to get more info...

Note: this answer has been edited to make it clearer after discussion in comments/chat.

asgoth
  • 35,552
  • 12
  • 89
  • 98
MikO
  • 18,243
  • 12
  • 77
  • 109
  • I mean I am doing lot of other things in `Holder` and that is needed basically. Also I cannot have pojo for Holder class bcoz I can have many more fields in that class and this json comes from a database fetch every 15 minutes.. Keeping these in mind what is the best way to do that? – john Dec 08 '16 at 15:08
  • @david and having those 2 attributes `linkedTo` and `decode` in your `Holder` class, along with all the other stuff you have there, doesn't work? – MikO Dec 08 '16 at 15:23
  • I mean having those fields _instead of_ your `data` field. Note that according to your JSON, `Holder` class does _not_ contain a `Map` called `data`... there's no `data` field anywhere in your JSON! – MikO Dec 08 '16 at 15:29
  • I mean this json come from database every 15-20 minutes so if we add any new stuff in this json then we have to come and change the pojo for it every time so to make this generic, that is why we are loading it in that structure.. Apart from making POJO what is the other best way or can we simplify my code above? – john Dec 08 '16 at 15:32
  • @david ok I added an edit, that's the only thing I can think of. But as I said it's been years since I last used Gson (since I last used Java actually!) so not sure 100%, but iirc something like that should work - it's not as simple as my previous suggestions, but much simpler than yours anyway... hope it helps... – MikO Dec 08 '16 at 16:57
  • I have a quick question. I just noticed one thing (which is not a big issue at all) and wanted to see if there is any way to do this. So `deserialize` method takes `JsonElement` as input parameter and that in my case will come as `{"linkedTo":"COUNT"}` and then it get loaded into `data` map as `{linkedTo=COUNT}`. I wanted to see if there is any way by which all the value of `data` map can be lowercase so instead of this `{linkedTo=COUNT}`, it should get stored like this `{linkedTo=count}` in `data` map automatically? – john Dec 17 '16 at 18:59
  • Any idea whether it can be done in the current implementation? – john Dec 30 '16 at 18:44