I have an object with the following attributes.
private final String messageBundle;
private final List messageParams;
private final String acti
After several hours of gooling and coding - there is my version:
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));
}
in.nextNull();
return Optional.empty();
}
}
You can simple registered it with GsonBuilder
like this:
instance.registerTypeAdapterFactory(OptionalTypeAdapter.FACTORY)
Please keep attention that Gson does not set values to your class field if field does not present in json. So you need to set default value Optional.empty()
in your entity.
I'll add to Anton Onikiychuk's answer
@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));
}
Just as an addition to maaartinus solution, the version without the encapsulating list, where Optional.absent
is simply serialized as 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());
}
}
The solution by Ilya ignores type parameters, so it can't really work in the general case. My solution is rather complicated, because of the need to distinguish between null
and Optional.absent()
-- otherwise you could strip away the encapsulation as a list.
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;
}
}
you may want to try this gson module, which can deal with java8 optional types serialization/deserialization(and als java8 new date type).