Fixed nullSafe usage. (#1555)

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>
This commit is contained in:
bufistov 2022-08-05 16:33:05 +02:00 committed by GitHub
parent 98f2bbf4c1
commit 46b97bf156
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 32 additions and 5 deletions

View File

@ -55,6 +55,7 @@ public final class JsonAdapterAnnotationTypeAdapterFactory implements TypeAdapte
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) {
@ -66,7 +67,8 @@ public final class JsonAdapterAnnotationTypeAdapterFactory implements TypeAdapte
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()
@ -74,7 +76,7 @@ public final class JsonAdapterAnnotationTypeAdapterFactory implements TypeAdapte
+ " JsonSerializer or JsonDeserializer.");
}
if (typeAdapter != null && annotation.nullSafe()) {
if (typeAdapter != null && nullSafe) {
typeAdapter = typeAdapter.nullSafe();
}

View File

@ -45,17 +45,24 @@ 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 {
@ -63,7 +70,7 @@ public final class TreeTypeAdapter<T> extends TypeAdapter<T> {
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);
@ -74,7 +81,7 @@ public final class TreeTypeAdapter<T> extends TypeAdapter<T> {
delegate().write(out, value);
return;
}
if (value == null) {
if (nullSafe && value == null) {
out.nullValue();
return;
}

View File

@ -161,4 +161,22 @@ public final class JsonAdapterSerializerDeserializerTest extends TestCase {
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;
}
}
}