Как записать значение JsonElement в пользовательском десериализаторе Gson?

У меня есть собственный десериализатор для моего класса, как показано ниже:

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();
    // in the below data map, I want value to be stored in lowercase
    // how can I do that?
    Map<String, String> data = context.deserialize(json, mapType);
    return new Holder(data);
  }  
}

И вот как я регистрирую свой десериализатор при создании объекта Gson:

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

И, наконец, разбор моего JSON вот так:

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

В моем методе deserialize значение json поступает так же, как этот {"linkedTo":"COUNT"}, а затем загружается в карту данных как {linkedTo=COUNT}. Я хотел посмотреть, есть ли способ, которым все значение карты data может быть строчным, поэтому вместо этого {linkedTo=COUNT} он должен автоматически сохраняться таким образом {linkedTo=COUNT} в карте данных?

Есть ли способ сделать это в Gson автоматически?

Update:

Ниже мой контент JSON:

{
    "abc": {
        "linkedTo": "COUNT",
        // possibly more data...
    },
    "plmtq": {
        "linkedTo": "TITLE",
        "decode": "TRUE",
        // possibly more data...
    }
}

Ответ 1

Во-первых, предлагается использовать Gso TypeAdapter вместо JsonDeserializer. Поэтому я собираюсь ответить на ваш вопрос:

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

Дополнительная информация.

Вопрос: Как мы можем изменить содержимое json перед десериализацией?

Одно из решений: Предварительно обработать содержимое json перед десериализацией и изменить часть его содержимого.

Как мы можем добиться этого с помощью TypeAdapter: Определить пользовательский TypeAdapter, получить json-контент по методу read (который вызывается непосредственно перед десериализацией) и изменять содержимое.

Пример кода:

Определите TypeAdapterFactory и a TypeAdapter;

TypeAdapterFactory myCustomTypeAdapterFactory = new TypeAdapterFactory() {
            @Override
            public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {

                final TypeAdapter<JsonElement> elementAdapter = gson.getAdapter(JsonElement.class);
                final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type); //

                return new TypeAdapter<T>() {

                    public void write(JsonWriter out, T value) throws IOException {
                        JsonElement tree = delegate.toJsonTree(value);
                        beforeWrite(value, tree);
                        elementAdapter.write(out, tree);
                    }

                    public T read(JsonReader in) throws IOException {
                        JsonElement tree = elementAdapter.read(in);
                        afterRead(tree);
                        return delegate.fromJsonTree(tree);
                    }

                    /**
                     * Modify {@code toSerialize} before it is written to
                     * the outgoing JSON stream.
                     */
                    protected void beforeWrite(T source, JsonElement toSerialize) {

                    }

                    /**
                     * Modify {@code deserialized} before it is parsed
                     */
                    protected void afterRead(JsonElement deserialized) {
                        if(deserialized instanceof JsonObject) {
                            JsonObject jsonObject = ((JsonObject)deserialized);
                            Set<Map.Entry<String, JsonElement>> entrySet = jsonObject.entrySet();
                            for(Map.Entry<String,JsonElement> entry : entrySet){
                                if(entry.getValue() instanceof JsonPrimitive) {
                                    if(entry.getKey().equalsIgnoreCase("linkedTo")) {
                                        String val = jsonObject.get(entry.getKey()).toString();
                                        jsonObject.addProperty(entry.getKey(), val.toLowerCase());
                                    }
                                } else {
                                    afterRead(entry.getValue());
                                }
                            }
                        }
                    }
                };
            }
        };

Мы добавили дополнительный процесс перед десериализацией. Мы получаем entrySet из содержимого json и обновляем значение ключа linkedTo.

Рабочий пример:

String jsonContent = "{\"abc\":{\"linkedTo\":\"COUNT\"},\"plmtq\":{\"linkedTo\":\"TITLE\",\"decode\":\"TRUE\"}}";

GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapterFactory(myCustomTypeAdapterFactory);
Gson gson = gsonBuilder.create();

Map mapDeserialized = gson.fromJson(jsonContent, Map.class);

Вывод:

введите описание изображения здесь

Это аналогичный ответ на ваш вопрос.