Используя Gson, как я могу сериализовать нули в картах, но не в других классах? - PullRequest
1 голос
/ 09 ноября 2019

Используя Gson, я хочу иметь возможность сериализации нулей в Map с, но не в других классах. То есть, если у меня есть такой класс:

public class Foo {
  @SerializedName("bar")
  Bar bar;

  @SerializedName("my_map")
  Map<String, Object> myMap;
}

Если поле bar равно нулю и myMap содержит одну запись "key" -> null, я хочу, чтобы полученная строка JSON была:

{
  "my_map": {
    "key": null,
  }
}

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

Я пробовал следующее:

  • с использованием Gson gson = new GsonBuilder().serializeNulls().create(). Это не работает, потому что сериализует все нулевые значения.

  • с использованием TypeAdapterFactory. Это почти работает, но я не могу найти способ повторно использовать Gson по умолчанию MapTypeAdapter с контекстом сериализации, где serializeNulls = true. MapTypeAdapter - это final, поэтому я не могу сделать его подклассом. Я не хочу писать TypeAdapter с нуля для сериализации карт.

Я чувствую, что есть простое решение, которое мне не хватает. Любая помощь приветствуется.

1 Ответ

1 голос
/ 14 ноября 2019

Вы должны заметить, что функцию SerializeNulls мы можем установить для экземпляра com.google.gson.stream.JsonWriter напрямую. Итак, давайте реализуем пользовательский com.google.gson.TypeAdapterFactory, который всегда включает эту функцию для Map экземпляров:

class ForceNullsForMapTypeAdapterFactory implements TypeAdapterFactory {

    public final <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
        if (Map.class.isAssignableFrom(type.getRawType())) {
            final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);
            return createCustomTypeAdapter(delegate);
        }

        return null;
    }

    private <T> TypeAdapter<T> createCustomTypeAdapter(TypeAdapter<T> delegate) {
        return new TypeAdapter<T>() {
            @Override
            public void write(JsonWriter out, T value) throws IOException {
                final boolean serializeNulls = out.getSerializeNulls();
                try {
                    out.setSerializeNulls(true);
                    delegate.write(out, value);
                } finally {
                    out.setSerializeNulls(serializeNulls);
                }
            }

            @Override
            public T read(JsonReader in) throws IOException {
                return delegate.read(in);
            }
        };
    }
}

Простое использование:

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.annotations.SerializedName;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;

import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;

public class GsonApp {
    public static void main(String[] args) {
        Map<String, Object> map = new LinkedHashMap<>();
        map.put("nullKey", null);
        map.put("other", "Not Null!");

        Foo foo = new Foo();
        foo.setMyMap(map);

        Gson gson = new GsonBuilder()
                .setPrettyPrinting()
                .registerTypeAdapterFactory(new ForceNullsForMapTypeAdapterFactory())
                .create();
        System.out.println(gson.toJson(foo));
    }
}

Надпечатки кода выше:

{
  "my_map": {
    "nullKey": null,
    "other": "Not Null!"
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...