Как сериализовать дополнительные классы <T> с Gson?
У меня есть объект со следующими атрибутами.
private final String messageBundle;
private final List<String> messageParams;
private final String actionBundle;
private final Map<String, String> data;
private final Optional<Pair<Integer,TimeUnit>> ttl;
private final Optional<Integer> badgeNumber;
private final Optional<String> collapseKey;
Объект находится в библиотеке, я бы предпочел не изменять его только для целей сериализации и хотел бы избежать затрат на создание другого DTO.
Как я могу сериализовать /unserialize Необязательные атрибуты? Необязательный не имеет конструктора по умолчанию (ни apache commons Pair), но я не могу использовать InstanceCreator и не понимаю, как создать TypeAdapter, который просто делегирует сериализацию базовому необязательному контенту.
Ответы
Ответ 1
Решение Илья игнорирует параметры типа, поэтому в общем случае он не может работать. Мое решение довольно сложно из-за необходимости различать null
и Optional.absent()
- в противном случае вы могли бы исключить инкапсуляцию в виде списка.
public class GsonOptionalDeserializer<T>
implements JsonSerializer<Optional<T>>, JsonDeserializer<Optional<T>> {
@Override
public Optional<T> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
final JsonArray asJsonArray = json.getAsJsonArray();
final JsonElement jsonElement = asJsonArray.get(0);
final T value = context.deserialize(jsonElement, ((ParameterizedType) typeOfT).getActualTypeArguments()[0]);
return Optional.fromNullable(value);
}
@Override
public JsonElement serialize(Optional<T> src, Type typeOfSrc, JsonSerializationContext context) {
final JsonElement element = context.serialize(src.orNull());
final JsonArray result = new JsonArray();
result.add(element);
return result;
}
}
Ответ 2
После нескольких часов головок и кодирования - есть моя версия:
public class OptionalTypeAdapter<E> extends TypeAdapter<Optional<E>> {
public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() {
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
Class<T> rawType = (Class<T>) type.getRawType();
if (rawType != Optional.class) {
return null;
}
final ParameterizedType parameterizedType = (ParameterizedType) type.getType();
final Type actualType = parameterizedType.getActualTypeArguments()[0];
final TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(actualType));
return new OptionalTypeAdapter(adapter);
}
};
private final TypeAdapter<E> adapter;
public OptionalTypeAdapter(TypeAdapter<E> adapter) {
this.adapter = adapter;
}
@Override
public void write(JsonWriter out, Optional<E> value) throws IOException {
if(value.isPresent()){
adapter.write(out, value.get());
} else {
out.nullValue();
}
}
@Override
public Optional<E> read(JsonReader in) throws IOException {
final JsonToken peek = in.peek();
if(peek != JsonToken.NULL){
return Optional.ofNullable(adapter.read(in));
}
return Optional.empty();
}
}
Вы можете просто зарегистрировать его с помощью GsonBuilder
следующим образом:
instance.registerTypeAdapterFactory(OptionalTypeAdapter.FACTORY)
Обратите внимание, что Gson не устанавливает значения в поле вашего класса, если поле не присутствует в json. Поэтому вам нужно установить значение по умолчанию Optional.empty()
в вашей сущности.
Ответ 3
вы можете попробовать этот модуль gson, который может обрабатывать сериализацию/десериализацию необязательных типов java8 (и новый тип даты alas java8).
Ответ 4
Так же, как дополнение к решению maaartinus, версия без инкапсулирующего списка, где Optional.absent
просто сериализуется как null
:
public class GsonOptionalDeserializer<T> implements JsonSerializer<Optional<T>>, JsonDeserializer<Optional<T>> {
@Override
public Optional<T> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
final T value = context.deserialize(json, ((ParameterizedType) typeOfT).getActualTypeArguments()[0]);
return Optional.fromNullable(value);
}
@Override
public JsonElement serialize(Optional<T> src, Type typeOfSrc, JsonSerializationContext context) {
return context.serialize(src.orNull());
}
}
Ответ 5
Я добавлю к Антону Оникичук ответ
@Override
public Optional<E> read(JsonReader in) throws IOException {
JsonToken peek = in.peek();
if (peek == JsonToken.NULL) {
in.nextNull(); // consuming JSON null
return Optional.empty();
}
return Optional.ofNullable(adapter.read(in));
}