Fixed issue 68 by providing support to override default type adapters for
primitive types. Added a visitFieldUsingCustomHandler method to handle this in the visitors.
This commit is contained in:
parent
70c7728218
commit
cc90a68241
@ -143,9 +143,6 @@ public final class Gson {
|
||||
/**
|
||||
* Constructs a Gson object with the specified version and the mode of operation while
|
||||
* encountering inner class references.
|
||||
*
|
||||
* @param factory the object navigator factory to use when creating a new {@link ObjectNavigator}
|
||||
* instance
|
||||
*/
|
||||
Gson(ExclusionStrategy strategy, FieldNamingStrategy fieldNamingPolicy) {
|
||||
this(strategy, fieldNamingPolicy, createObjectConstructor(DefaultTypeAdapters.DEFAULT_INSTANCE_CREATORS),
|
||||
|
@ -131,4 +131,8 @@ final class JsonArrayDeserializationVisitor<T> extends JsonDeserializationVisito
|
||||
public void visitPrimitiveField(Field f, Type typeOfF, Object obj) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public boolean visitFieldUsingCustomHandler(Field f, Type actualTypeOfField, Object parent) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ abstract class JsonDeserializationVisitor<T> implements ObjectNavigator.Visitor
|
||||
protected T target;
|
||||
protected final JsonElement json;
|
||||
protected final Type targetType;
|
||||
private final JsonDeserializationContext context;
|
||||
protected final JsonDeserializationContext context;
|
||||
|
||||
public JsonDeserializationVisitor(JsonElement json, Type targetType,
|
||||
ObjectNavigatorFactory factory, ObjectConstructor objectConstructor, TypeAdapter typeAdapter,
|
||||
|
@ -147,4 +147,24 @@ final class JsonObjectDeserializationVisitor<T> extends JsonDeserializationVisit
|
||||
FieldNamingStrategy namingPolicy = factory.getFieldNamingPolicy();
|
||||
return namingPolicy.translateName(f);
|
||||
}
|
||||
|
||||
public boolean visitFieldUsingCustomHandler(Field f, Type actualTypeOfField, Object parent) {
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
JsonDeserializer deserializer = deserializers.getHandlerFor(actualTypeOfField);
|
||||
if (deserializer != null) {
|
||||
String fName = getFieldName(f);
|
||||
JsonElement child = json.getAsJsonObject().get(fName);
|
||||
if (child == null) {
|
||||
child = JsonNull.INSTANCE;
|
||||
}
|
||||
Object value = deserializer.deserialize(child, actualTypeOfField, context);
|
||||
f.set(parent, value);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -92,4 +92,9 @@ final class JsonPrimitiveDeserializationVisitor<T> extends JsonDeserializationVi
|
||||
// should not be called since this case should invoke JsonArrayDeserializationVisitor
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
public boolean visitFieldUsingCustomHandler(Field f, Type actualTypeOfField, Object parent) {
|
||||
// should not be called since this case should invoke JsonObjectDeserializationVisitor
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
|
@ -191,12 +191,36 @@ final class JsonSerializationVisitor implements ObjectNavigator.Visitor {
|
||||
serializer = serializers.getHandlerFor(Map.class);
|
||||
}
|
||||
if (serializer != null) {
|
||||
assignToRoot(serializer.serialize(obj, objType, context));
|
||||
if (obj == null) {
|
||||
assignToRoot(JsonNull.INSTANCE);
|
||||
} else {
|
||||
assignToRoot(serializer.serialize(obj, objType, context));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public boolean visitFieldUsingCustomHandler(Field f, Type actualTypeOfField, Object parent) {
|
||||
try {
|
||||
Preconditions.checkState(root.isJsonObject());
|
||||
Object obj = f.get(parent);
|
||||
JsonSerializer serializer = serializers.getHandlerFor(actualTypeOfField);
|
||||
if (serializer == null && obj instanceof Map) {
|
||||
serializer = serializers.getHandlerFor(Map.class);
|
||||
}
|
||||
if (serializer != null) {
|
||||
JsonElement child = serializer.serialize(obj, actualTypeOfField, context);
|
||||
addChildAsElement(f, child);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
private void assignToRoot(JsonElement newRoot) {
|
||||
Preconditions.checkArgument(root == null);
|
||||
root = newRoot;
|
||||
|
@ -87,6 +87,11 @@ final class ObjectNavigator {
|
||||
* @return true if a custom handler exists, false otherwise
|
||||
*/
|
||||
public boolean visitUsingCustomHandler(Object obj, Type objType);
|
||||
|
||||
/**
|
||||
* This is called to visit a field of the current object using a custom handler
|
||||
*/
|
||||
public boolean visitFieldUsingCustomHandler(Field f, Type actualTypeOfField, Object parent);
|
||||
}
|
||||
|
||||
private final ExclusionStrategy exclusionStrategy;
|
||||
@ -130,23 +135,24 @@ final class ObjectNavigator {
|
||||
ancestors.push(obj);
|
||||
|
||||
try {
|
||||
if (objTypeInfo.isCollectionOrArray()) {
|
||||
if (objTypeInfo.isArray()) {
|
||||
visitor.visitArray(obj, objType);
|
||||
} else { // must be a collection
|
||||
visitor.visitCollection((Collection<?>) obj, objType);
|
||||
}
|
||||
} else if (objTypeInfo.isEnum()) {
|
||||
visitor.visitEnum(obj, objType);
|
||||
} else if (objTypeInfo.isPrimitiveOrStringAndNotAnArray()) {
|
||||
visitor.visitPrimitiveValue(obj);
|
||||
} else {
|
||||
if (!visitor.visitUsingCustomHandler(obj, objType)) {
|
||||
boolean visitedWithCustomHandler = visitor.visitUsingCustomHandler(obj, objType);
|
||||
if (!visitedWithCustomHandler) {
|
||||
if (objTypeInfo.isCollectionOrArray()) {
|
||||
if (objTypeInfo.isArray()) {
|
||||
visitor.visitArray(obj, objType);
|
||||
} else { // must be a collection
|
||||
visitor.visitCollection((Collection<?>) obj, objType);
|
||||
}
|
||||
} else if (objTypeInfo.isEnum()) {
|
||||
visitor.visitEnum(obj, objType);
|
||||
} else if (objTypeInfo.isPrimitiveOrStringAndNotAnArray()) {
|
||||
visitor.visitPrimitiveValue(obj);
|
||||
} else {
|
||||
visitor.startVisitingObject(obj);
|
||||
// For all classes in the inheritance hierarchy (including the current class),
|
||||
// visit all fields
|
||||
for (Class<?> curr = objTypeInfo.getRawClass();
|
||||
curr != null && !curr.equals(Object.class); curr = curr.getSuperclass()) {
|
||||
curr != null && !curr.equals(Object.class); curr = curr.getSuperclass()) {
|
||||
if (!curr.isSynthetic()) {
|
||||
navigateClassFields(obj, curr, visitor);
|
||||
}
|
||||
@ -167,16 +173,22 @@ final class ObjectNavigator {
|
||||
Type actualTypeOfField = fieldTypeInfo.getActualType();
|
||||
if (exclusionStrategy.shouldSkipField(f)) {
|
||||
continue; // skip
|
||||
} else if (fieldTypeInfo.isCollectionOrArray()) {
|
||||
if (fieldTypeInfo.isArray()) {
|
||||
visitor.visitArrayField(f, actualTypeOfField, obj);
|
||||
} else { // must be Collection
|
||||
visitor.visitCollectionField(f, actualTypeOfField, obj);
|
||||
}
|
||||
} else if (fieldTypeInfo.isPrimitiveOrStringAndNotAnArray()) {
|
||||
visitor.visitPrimitiveField(f, actualTypeOfField, obj);
|
||||
} else {
|
||||
visitor.visitObjectField(f, actualTypeOfField, obj);
|
||||
boolean visitedWithCustomHandler =
|
||||
visitor.visitFieldUsingCustomHandler(f, actualTypeOfField, obj);
|
||||
if (!visitedWithCustomHandler) {
|
||||
if (fieldTypeInfo.isCollectionOrArray()) {
|
||||
if (fieldTypeInfo.isArray()) {
|
||||
visitor.visitArrayField(f, actualTypeOfField, obj);
|
||||
} else { // must be Collection
|
||||
visitor.visitCollectionField(f, actualTypeOfField, obj);
|
||||
}
|
||||
} else if (fieldTypeInfo.isPrimitiveOrStringAndNotAnArray()) {
|
||||
visitor.visitPrimitiveField(f, actualTypeOfField, obj);
|
||||
} else {
|
||||
visitor.visitObjectField(f, actualTypeOfField, obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -39,4 +39,10 @@ final class Preconditions {
|
||||
throw new IllegalArgumentException("condition failed: " + condition);
|
||||
}
|
||||
}
|
||||
|
||||
public static void checkState(boolean condition) {
|
||||
if (!condition) {
|
||||
throw new IllegalArgumentException("condition failed: " + condition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonNull;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
@ -196,6 +197,27 @@ public class CustomTypeAdaptersTest extends TestCase {
|
||||
}
|
||||
}
|
||||
|
||||
public void testCustomSerializerForLong() {
|
||||
final ClassWithBooleanField customSerializerInvoked = new ClassWithBooleanField();
|
||||
customSerializerInvoked.value = false;
|
||||
Gson gson = new GsonBuilder().registerTypeAdapter(Long.class, new JsonSerializer<Long>() {
|
||||
public JsonElement serialize(Long src, Type typeOfSrc, JsonSerializationContext context) {
|
||||
customSerializerInvoked.value = true;
|
||||
return src == null ? new JsonNull() : new JsonPrimitive(src);
|
||||
}
|
||||
}).serializeNulls().create();
|
||||
ClassWithWrapperLongField src = new ClassWithWrapperLongField();
|
||||
String json = gson.toJson(src);
|
||||
assertTrue(json.contains("\"value\":null"));
|
||||
assertTrue(customSerializerInvoked.value);
|
||||
|
||||
customSerializerInvoked.value = false;
|
||||
src.value = 10L;
|
||||
json = gson.toJson(src);
|
||||
assertTrue(json.contains("\"value\":10"));
|
||||
assertTrue(customSerializerInvoked.value);
|
||||
}
|
||||
|
||||
public void testCustomDeserializerForLong() {
|
||||
final ClassWithBooleanField customDeserializerInvoked = new ClassWithBooleanField();
|
||||
customDeserializerInvoked.value = false;
|
||||
@ -203,14 +225,24 @@ public class CustomTypeAdaptersTest extends TestCase {
|
||||
public Long deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
|
||||
throws JsonParseException {
|
||||
customDeserializerInvoked.value = true;
|
||||
String str = json.getAsJsonPrimitive().getAsString();
|
||||
return str.length() == 0 ? null : Long.parseLong(str);
|
||||
if (json == null || json.isJsonNull()) {
|
||||
return null;
|
||||
} else {
|
||||
Number number = json.getAsJsonPrimitive().getAsNumber();
|
||||
return number == null ? null : number.longValue();
|
||||
}
|
||||
}
|
||||
}).create();
|
||||
String json = "{'value':null}";
|
||||
ClassWithWrapperLongField target = gson.fromJson(json, ClassWithWrapperLongField.class);
|
||||
assertNull(target.value);
|
||||
assertTrue(customDeserializerInvoked.value);
|
||||
|
||||
customDeserializerInvoked.value = false;
|
||||
json = "{'value':10}";
|
||||
target = gson.fromJson(json, ClassWithWrapperLongField.class);
|
||||
assertEquals(10L, target.value.longValue());
|
||||
assertTrue(customDeserializerInvoked.value);
|
||||
}
|
||||
|
||||
private static class ClassWithWrapperLongField {
|
||||
|
Loading…
Reference in New Issue
Block a user