Пользовательская десериализация на карте с использованием Gson

У меня есть JSON ниже:

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

Мне нужно загрузить этот JSON в Map<String, Holder>, с ключами "abc" и "plmtq".
Ниже мой класс Holder:

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

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

  // More methods in this class...
}

JSON не соответствует моей структуре классов, но я не могу изменить JSON или классы.
Мне нужен способ настройки десериализации, поэтому я могу разобрать JSON в Map<String, Holder>.
Есть ли способ сделать это?


Ниже приведен код, который делает это, но он выглядит слишком сложным и должен быть более простой способ сделать это...

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

Если вам действительно нужно сохранить класс Holder как есть, единственный вариант, о котором я могу думать, - создать собственный десериализатор для вашего класса Holder.

Таким образом, всякий раз, когда вы говорите Gson для разбора некоторого JSON в объекте Holder, он будет использовать ваш десериализатор вместо того, чтобы делать это "обычным способом" (т.е. сопоставление ключей JSON с объектными свойствами).

Что-то вроде этого:

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

Затем вы регистрируете свой десериализатор при создании объекта Gson:

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

И, наконец, вы разбираете свой JSON следующим образом:

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

Вы можете посмотреть документы Gson для пользовательских (de) serialization и этот другой ответьте, чтобы узнать больше...

Примечание: этот ответ был отредактирован, чтобы сделать его более понятным после обсуждения в комментариях/чате.