Ensured that custom deserializer is invoked with actual type returned for field values.
Fixed a bug where a top-level object was constructed with default values even when the custom deserializer tried to set it to null.
This commit is contained in:
parent
338758a0d3
commit
bc1e5c5c99
|
@ -35,6 +35,7 @@ abstract class JsonDeserializationVisitor<T> implements ObjectNavigator.Visitor
|
||||||
protected final JsonElement json;
|
protected final JsonElement json;
|
||||||
protected final Type targetType;
|
protected final Type targetType;
|
||||||
protected final JsonDeserializationContext context;
|
protected final JsonDeserializationContext context;
|
||||||
|
protected boolean constructed;
|
||||||
|
|
||||||
public JsonDeserializationVisitor(JsonElement json, Type targetType,
|
public JsonDeserializationVisitor(JsonElement json, Type targetType,
|
||||||
ObjectNavigatorFactory factory, ObjectConstructor objectConstructor,
|
ObjectNavigatorFactory factory, ObjectConstructor objectConstructor,
|
||||||
|
@ -47,11 +48,13 @@ abstract class JsonDeserializationVisitor<T> implements ObjectNavigator.Visitor
|
||||||
this.deserializers = deserializers;
|
this.deserializers = deserializers;
|
||||||
this.json = json;
|
this.json = json;
|
||||||
this.context = context;
|
this.context = context;
|
||||||
|
this.constructed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public T getTarget() {
|
public T getTarget() {
|
||||||
if (target == null) {
|
if (!constructed) {
|
||||||
target = constructTarget();
|
target = constructTarget();
|
||||||
|
constructed = true;
|
||||||
}
|
}
|
||||||
return target;
|
return target;
|
||||||
}
|
}
|
||||||
|
@ -70,14 +73,21 @@ abstract class JsonDeserializationVisitor<T> implements ObjectNavigator.Visitor
|
||||||
if (pair == null) {
|
if (pair == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!json.isJsonNull()) {
|
Object value = invokeCustomDeserializer(json, pair);
|
||||||
JsonDeserializer deserializer = pair.getFirst();
|
target = (T) value;
|
||||||
Type objType = pair.getSecond().getType();
|
constructed = true;
|
||||||
target = (T) deserializer.deserialize(json, objType, context);
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected Object invokeCustomDeserializer(JsonElement element,
|
||||||
|
Pair<JsonDeserializer<?>, ObjectTypePair> pair) {
|
||||||
|
if (element == null || element.isJsonNull()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Type objType = pair.getSecond().getType();
|
||||||
|
return (pair.getFirst()).deserialize(element, objType, context);
|
||||||
|
}
|
||||||
|
|
||||||
final Object visitChildAsObject(Type childType, JsonElement jsonChild) {
|
final Object visitChildAsObject(Type childType, JsonElement jsonChild) {
|
||||||
JsonDeserializationVisitor<?> childVisitor =
|
JsonDeserializationVisitor<?> childVisitor =
|
||||||
new JsonObjectDeserializationVisitor<Object>(jsonChild, childType,
|
new JsonObjectDeserializationVisitor<Object>(jsonChild, childType,
|
||||||
|
|
|
@ -93,32 +93,32 @@ final class JsonObjectDeserializationVisitor<T> extends JsonDeserializationVisit
|
||||||
return namingPolicy.translateName(f);
|
return namingPolicy.translateName(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean visitFieldUsingCustomHandler(Field f, Type actualTypeOfField, Object parent) {
|
public boolean visitFieldUsingCustomHandler(Field f, Type declaredTypeOfField, Object parent) {
|
||||||
try {
|
try {
|
||||||
String fName = getFieldName(f);
|
String fName = getFieldName(f);
|
||||||
if (!json.isJsonObject()) {
|
if (!json.isJsonObject()) {
|
||||||
throw new JsonParseException("Expecting object found: " + json);
|
throw new JsonParseException("Expecting object found: " + json);
|
||||||
}
|
}
|
||||||
JsonElement child = json.getAsJsonObject().get(fName);
|
JsonElement child = json.getAsJsonObject().get(fName);
|
||||||
if (child == null) {
|
TypeInfo typeInfo = new TypeInfo(declaredTypeOfField);
|
||||||
|
if (child == null) { // Child will be null if the field wasn't present in Json
|
||||||
return true;
|
return true;
|
||||||
} else if (child.isJsonNull()) {
|
} else if (child.isJsonNull()) {
|
||||||
TypeInfo typeInfo = new TypeInfo(actualTypeOfField);
|
|
||||||
if (!typeInfo.isPrimitive()) {
|
if (!typeInfo.isPrimitive()) {
|
||||||
f.set(parent, null);
|
f.set(parent, null);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@SuppressWarnings("unchecked")
|
ObjectTypePair objTypePair = new ObjectTypePair(null, declaredTypeOfField, false);
|
||||||
JsonDeserializer deserializer = deserializers.getHandlerFor(actualTypeOfField);
|
Pair<JsonDeserializer<?>, ObjectTypePair> pair = objTypePair.getMatchingHandler(deserializers);
|
||||||
if (deserializer != null) {
|
if (pair == null) {
|
||||||
if (!child.isJsonNull()) {
|
return false;
|
||||||
Object value = deserializer.deserialize(child, actualTypeOfField, context);
|
|
||||||
f.set(parent, value);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
Object value = invokeCustomDeserializer(child, pair);
|
||||||
|
if (value != null || !typeInfo.isPrimitive()) {
|
||||||
|
f.set(parent, value);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
} catch (IllegalAccessException e) {
|
} catch (IllegalAccessException e) {
|
||||||
throw new RuntimeException();
|
throw new RuntimeException();
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,8 @@ import com.google.gson.JsonDeserializer;
|
||||||
import com.google.gson.JsonElement;
|
import com.google.gson.JsonElement;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import com.google.gson.JsonParseException;
|
import com.google.gson.JsonParseException;
|
||||||
|
import com.google.gson.common.TestTypes.Base;
|
||||||
|
import com.google.gson.common.TestTypes.ClassWithBaseField;
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
@ -144,4 +146,90 @@ public class CustomDeserializerTest extends TestCase {
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
String field2;
|
String field2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testCustomDeserializerReturnsNullForTopLevelObject() {
|
||||||
|
Gson gson = new GsonBuilder()
|
||||||
|
.registerTypeAdapter(Base.class, new JsonDeserializer<Base>() {
|
||||||
|
public Base deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
|
||||||
|
throws JsonParseException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}).create();
|
||||||
|
String json = "{baseName:'Base',subName:'SubRevised'}";
|
||||||
|
Base target = gson.fromJson(json, Base.class);
|
||||||
|
assertNull(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCustomDeserializerReturnsNull() {
|
||||||
|
Gson gson = new GsonBuilder()
|
||||||
|
.registerTypeAdapter(Base.class, new JsonDeserializer<Base>() {
|
||||||
|
public Base deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
|
||||||
|
throws JsonParseException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}).create();
|
||||||
|
String json = "{base:{baseName:'Base',subName:'SubRevised'}}";
|
||||||
|
ClassWithBaseField target = gson.fromJson(json, ClassWithBaseField.class);
|
||||||
|
assertNull(target.base);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCustomDeserializerReturnsNullForTopLevelPrimitives() {
|
||||||
|
Gson gson = new GsonBuilder()
|
||||||
|
.registerTypeAdapter(long.class, new JsonDeserializer<Long>() {
|
||||||
|
public Long deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
|
||||||
|
throws JsonParseException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}).create();
|
||||||
|
String json = "10";
|
||||||
|
assertNull(gson.fromJson(json, long.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCustomDeserializerReturnsNullForPrimitiveFields() {
|
||||||
|
Gson gson = new GsonBuilder()
|
||||||
|
.registerTypeAdapter(long.class, new JsonDeserializer<Long>() {
|
||||||
|
public Long deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
|
||||||
|
throws JsonParseException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}).create();
|
||||||
|
String json = "{field:10}";
|
||||||
|
ClassWithLong target = gson.fromJson(json, ClassWithLong.class);
|
||||||
|
assertEquals(0, target.field);
|
||||||
|
}
|
||||||
|
private static class ClassWithLong {
|
||||||
|
long field;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCustomDeserializerReturnsNullForArrayElements() {
|
||||||
|
Gson gson = new GsonBuilder()
|
||||||
|
.registerTypeAdapter(Base.class, new JsonDeserializer<Base>() {
|
||||||
|
public Base deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
|
||||||
|
throws JsonParseException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}).create();
|
||||||
|
String json = "[{baseName:'Base'},{baseName:'Base'}]";
|
||||||
|
Base[] target = gson.fromJson(json, Base[].class);
|
||||||
|
assertNull(target[0]);
|
||||||
|
assertNull(target[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCustomDeserializerReturnsNullForArrayElementsForArrayField() {
|
||||||
|
Gson gson = new GsonBuilder()
|
||||||
|
.registerTypeAdapter(Base.class, new JsonDeserializer<Base>() {
|
||||||
|
public Base deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
|
||||||
|
throws JsonParseException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}).create();
|
||||||
|
String json = "{bases:[{baseName:'Base'},{baseName:'Base'}]}";
|
||||||
|
ClassWithBaseArray target = gson.fromJson(json, ClassWithBaseArray.class);
|
||||||
|
assertNull(target.bases[0]);
|
||||||
|
assertNull(target.bases[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ClassWithBaseArray {
|
||||||
|
Base[] bases;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -200,7 +200,7 @@ public class NullObjectAndFieldTest extends TestCase {
|
||||||
}).create();
|
}).create();
|
||||||
String json = "{value:'value1'}";
|
String json = "{value:'value1'}";
|
||||||
ObjectWithField target = gson.fromJson(json, ObjectWithField.class);
|
ObjectWithField target = gson.fromJson(json, ObjectWithField.class);
|
||||||
assertFalse("value1".equals(target.value));
|
assertNull(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class ObjectWithField {
|
private static class ObjectWithField {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user