Custom deserialization into a Map using Gson

2019-03-03 23:10发布

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;
}

1条回答
Emotional °昔
2楼-- · 2019-03-03 23:18

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.

查看更多
登录 后发表回答