Skip to content

Commit

Permalink
Fixed nullSafe usage. (#1555)
Browse files Browse the repository at this point in the history
The JsonSerializer/Deserializer adapters used to ignore this attribute
which result in inconsistent behaviour for annotated adapters.

Fixes #1553

Signed-off-by: Dmitry Bufistov <dmitry@midokura.com>

Co-authored-by: Dmitry Bufistov <dmitry@midokura.com>
  • Loading branch information
bufistov and Dmitry Bufistov committed Aug 5, 2022
1 parent 98f2bbf commit 46b97bf
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 5 deletions.
Expand Up @@ -55,6 +55,7 @@ TypeAdapter<?> getTypeAdapter(ConstructorConstructor constructorConstructor, Gso
Object instance = constructorConstructor.get(TypeToken.get(annotation.value())).construct();

TypeAdapter<?> typeAdapter;
boolean nullSafe = annotation.nullSafe();
if (instance instanceof TypeAdapter) {
typeAdapter = (TypeAdapter<?>) instance;
} else if (instance instanceof TypeAdapterFactory) {
Expand All @@ -66,15 +67,16 @@ TypeAdapter<?> getTypeAdapter(ConstructorConstructor constructorConstructor, Gso
JsonDeserializer<?> deserializer = instance instanceof JsonDeserializer
? (JsonDeserializer) instance
: null;
typeAdapter = new TreeTypeAdapter(serializer, deserializer, gson, type, null);
typeAdapter = new TreeTypeAdapter(serializer, deserializer, gson, type, null, nullSafe);
nullSafe = false;
} else {
throw new IllegalArgumentException("Invalid attempt to bind an instance of "
+ instance.getClass().getName() + " as a @JsonAdapter for " + type.toString()
+ ". @JsonAdapter value must be a TypeAdapter, TypeAdapterFactory,"
+ " JsonSerializer or JsonDeserializer.");
}

if (typeAdapter != null && annotation.nullSafe()) {
if (typeAdapter != null && nullSafe) {
typeAdapter = typeAdapter.nullSafe();
}

Expand Down
Expand Up @@ -45,25 +45,32 @@ public final class TreeTypeAdapter<T> extends TypeAdapter<T> {
private final TypeToken<T> typeToken;
private final TypeAdapterFactory skipPast;
private final GsonContextImpl context = new GsonContextImpl();
private final boolean nullSafe;

/** The delegate is lazily created because it may not be needed, and creating it may fail. */
private volatile TypeAdapter<T> delegate;

public TreeTypeAdapter(JsonSerializer<T> serializer, JsonDeserializer<T> deserializer,
Gson gson, TypeToken<T> typeToken, TypeAdapterFactory skipPast) {
Gson gson, TypeToken<T> typeToken, TypeAdapterFactory skipPast, boolean nullSafe) {
this.serializer = serializer;
this.deserializer = deserializer;
this.gson = gson;
this.typeToken = typeToken;
this.skipPast = skipPast;
this.nullSafe = nullSafe;
}

public TreeTypeAdapter(JsonSerializer<T> serializer, JsonDeserializer<T> deserializer,
Gson gson, TypeToken<T> typeToken, TypeAdapterFactory skipPast) {
this(serializer, deserializer, gson, typeToken, skipPast, true);
}

@Override public T read(JsonReader in) throws IOException {
if (deserializer == null) {
return delegate().read(in);
}
JsonElement value = Streams.parse(in);
if (value.isJsonNull()) {
if (nullSafe && value.isJsonNull()) {
return null;
}
return deserializer.deserialize(value, typeToken.getType(), context);
Expand All @@ -74,7 +81,7 @@ public TreeTypeAdapter(JsonSerializer<T> serializer, JsonDeserializer<T> deseria
delegate().write(out, value);
return;
}
if (value == null) {
if (nullSafe && value == null) {
out.nullValue();
return;
}
Expand Down
Expand Up @@ -161,4 +161,22 @@ private static final class BaseIntegerAdapter implements JsonSerializer<Base<Int
return new JsonPrimitive("BaseIntegerAdapter");
}
}

public void testJsonAdapterNullSafe() {
Gson gson = new Gson();
String json = gson.toJson(new Computer3(null, null));
assertEquals("{\"user1\":\"UserSerializerDeserializer\"}", json);
Computer3 computer3 = gson.fromJson("{\"user1\":null, \"user2\":null}", Computer3.class);
assertEquals("UserSerializerDeserializer", computer3.user1.name);
assertNull(computer3.user2);
}

private static final class Computer3 {
@JsonAdapter(value = UserSerializerDeserializer.class, nullSafe = false) final User user1;
@JsonAdapter(value = UserSerializerDeserializer.class) final User user2;
Computer3(User user1, User user2) {
this.user1 = user1;
this.user2 = user2;
}
}
}

0 comments on commit 46b97bf

Please sign in to comment.