Major restructuring of Primitve type serialization and deserializtion. From the end-user's point of view there should be no difference other than the user can now override the default serialization/deserialization their own custom type adapter (not sure if there is a real use-case out there for this).

This restructuring greatly cleans up the code and reduces some complexity; however, there is more that can be done to clean this up (i.e. get rid of "InstanceCreators" for primitive Type Adapters).
This commit is contained in:
Joel Leitch 2008-11-15 02:26:57 +00:00
parent 7e360b95e7
commit 0d8150fe52
20 changed files with 343 additions and 256 deletions

View File

@ -64,14 +64,16 @@ final class DefaultTypeAdapters {
private static final BigDecimalTypeAdapter BIG_DECIMAL_TYPE_ADAPTER = new BigDecimalTypeAdapter();
private static final BigIntegerTypeAdapter BIG_INTEGER_TYPE_ADAPTER = new BigIntegerTypeAdapter();
private static final BooleanCreator BOOLEAN_CREATOR = new BooleanCreator();
private static final ByteCreator BYTE_CREATOR = new ByteCreator();
private static final CharacterCreator CHARACTER_CREATOR = new CharacterCreator();
private static final DoubleCreator DOUBLE_CREATOR = new DoubleCreator();
private static final FloatCreator FLOAT_CREATOR = new FloatCreator();
private static final IntegerCreator INTEGER_CREATOR = new IntegerCreator();
private static final LongCreator LONG_CREATOR = new LongCreator();
private static final ShortCreator SHORT_CREATOR = new ShortCreator();
private static final BooleanTypeAdapter BOOLEAN_TYPE_ADAPTER = new BooleanTypeAdapter();
private static final ByteTypeAdapter BYTE_TYPE_ADAPTER = new ByteTypeAdapter();
private static final CharacterTypeAdapter CHARACTER_TYPE_ADAPTER = new CharacterTypeAdapter();
private static final DoubleTypeAdapter DOUBLE_TYPE_ADAPTER = new DoubleTypeAdapter();
private static final FloatTypeAdapter FLOAT_TYPE_ADAPTER = new FloatTypeAdapter();
private static final IntegerTypeAdapter INTEGER_TYPE_ADAPTER = new IntegerTypeAdapter();
private static final LongTypeAdapter LONG_TYPE_ADAPTER = new LongTypeAdapter();
private static final ShortTypeAdapter SHORT_TYPE_ADAPTER = new ShortTypeAdapter();
private static final StringTypeAdapter STRING_TYPE_ADAPTER = new StringTypeAdapter();
private static final LinkedListCreator LINKED_LIST_CREATOR = new LinkedListCreator();
private static final TreeSetCreator TREE_SET_CREATOR = new TreeSetCreator();
@ -98,6 +100,26 @@ final class DefaultTypeAdapters {
map.register(Date.class, DATE_TYPE_ADAPTER);
map.register(BigDecimal.class, BIG_DECIMAL_TYPE_ADAPTER);
map.register(BigInteger.class, BIG_INTEGER_TYPE_ADAPTER);
// Add primitive serializers
map.register(Boolean.class, BOOLEAN_TYPE_ADAPTER);
map.register(boolean.class, BOOLEAN_TYPE_ADAPTER);
map.register(Byte.class, BYTE_TYPE_ADAPTER);
map.register(byte.class, BYTE_TYPE_ADAPTER);
map.register(Character.class, CHARACTER_TYPE_ADAPTER);
map.register(char.class, CHARACTER_TYPE_ADAPTER);
map.register(Double.class, DOUBLE_TYPE_ADAPTER);
map.register(double.class, DOUBLE_TYPE_ADAPTER);
map.register(Float.class, FLOAT_TYPE_ADAPTER);
map.register(float.class, FLOAT_TYPE_ADAPTER);
map.register(Integer.class, INTEGER_TYPE_ADAPTER);
map.register(int.class, INTEGER_TYPE_ADAPTER);
map.register(Long.class, LONG_TYPE_ADAPTER);
map.register(long.class, LONG_TYPE_ADAPTER);
map.register(Short.class, SHORT_TYPE_ADAPTER);
map.register(short.class, SHORT_TYPE_ADAPTER);
map.register(String.class, STRING_TYPE_ADAPTER);
map.makeUnmodifiable();
return map;
}
@ -114,6 +136,26 @@ final class DefaultTypeAdapters {
map.register(Date.class, wrapDeserializer(DATE_TYPE_ADAPTER));
map.register(BigDecimal.class, wrapDeserializer(BIG_DECIMAL_TYPE_ADAPTER));
map.register(BigInteger.class, wrapDeserializer(BIG_INTEGER_TYPE_ADAPTER));
// Add primitive deserializers
map.register(Boolean.class, wrapDeserializer(BOOLEAN_TYPE_ADAPTER));
map.register(boolean.class, wrapDeserializer(BOOLEAN_TYPE_ADAPTER));
map.register(Byte.class, wrapDeserializer(BYTE_TYPE_ADAPTER));
map.register(byte.class, wrapDeserializer(BYTE_TYPE_ADAPTER));
map.register(Character.class, wrapDeserializer(CHARACTER_TYPE_ADAPTER));
map.register(char.class, wrapDeserializer(CHARACTER_TYPE_ADAPTER));
map.register(Double.class, wrapDeserializer(DOUBLE_TYPE_ADAPTER));
map.register(double.class, wrapDeserializer(DOUBLE_TYPE_ADAPTER));
map.register(Float.class, wrapDeserializer(FLOAT_TYPE_ADAPTER));
map.register(float.class, wrapDeserializer(FLOAT_TYPE_ADAPTER));
map.register(Integer.class, wrapDeserializer(INTEGER_TYPE_ADAPTER));
map.register(int.class, wrapDeserializer(INTEGER_TYPE_ADAPTER));
map.register(Long.class, wrapDeserializer(LONG_TYPE_ADAPTER));
map.register(long.class, wrapDeserializer(LONG_TYPE_ADAPTER));
map.register(Short.class, wrapDeserializer(SHORT_TYPE_ADAPTER));
map.register(short.class, wrapDeserializer(SHORT_TYPE_ADAPTER));
map.register(String.class, wrapDeserializer(STRING_TYPE_ADAPTER));
map.makeUnmodifiable();
return map;
}
@ -124,30 +166,31 @@ final class DefaultTypeAdapters {
map.register(Enum.class, ENUM_TYPE_ADAPTER);
map.register(URL.class, URL_TYPE_ADAPTER);
map.register(Locale.class, LOCALE_TYPE_ADAPTER);
map.register(Collection.class, COLLECTION_TYPE_ADAPTER);
map.register(Map.class, MAP_TYPE_ADAPTER);
map.register(BigDecimal.class, BIG_DECIMAL_TYPE_ADAPTER);
map.register(BigInteger.class, BIG_INTEGER_TYPE_ADAPTER);
// Add primitive instance creators
map.register(Boolean.class, BOOLEAN_CREATOR);
map.register(boolean.class, BOOLEAN_CREATOR);
map.register(Byte.class, BYTE_CREATOR);
map.register(byte.class, BYTE_CREATOR);
map.register(Character.class, CHARACTER_CREATOR);
map.register(char.class, CHARACTER_CREATOR);
map.register(Double.class, DOUBLE_CREATOR);
map.register(double.class, DOUBLE_CREATOR);
map.register(Float.class, FLOAT_CREATOR);
map.register(float.class, FLOAT_CREATOR);
map.register(Integer.class, INTEGER_CREATOR);
map.register(int.class, INTEGER_CREATOR);
map.register(Long.class, LONG_CREATOR);
map.register(long.class, LONG_CREATOR);
map.register(Short.class, SHORT_CREATOR);
map.register(short.class, SHORT_CREATOR);
map.register(Collection.class, LINKED_LIST_CREATOR);
map.register(Boolean.class, BOOLEAN_TYPE_ADAPTER);
map.register(boolean.class, BOOLEAN_TYPE_ADAPTER);
map.register(Byte.class, BYTE_TYPE_ADAPTER);
map.register(byte.class, BYTE_TYPE_ADAPTER);
map.register(Character.class, CHARACTER_TYPE_ADAPTER);
map.register(char.class, CHARACTER_TYPE_ADAPTER);
map.register(Double.class, DOUBLE_TYPE_ADAPTER);
map.register(double.class, DOUBLE_TYPE_ADAPTER);
map.register(Float.class, FLOAT_TYPE_ADAPTER);
map.register(float.class, FLOAT_TYPE_ADAPTER);
map.register(Integer.class, INTEGER_TYPE_ADAPTER);
map.register(int.class, INTEGER_TYPE_ADAPTER);
map.register(Long.class, LONG_TYPE_ADAPTER);
map.register(long.class, LONG_TYPE_ADAPTER);
map.register(Short.class, SHORT_TYPE_ADAPTER);
map.register(short.class, SHORT_TYPE_ADAPTER);
map.register(String.class, STRING_TYPE_ADAPTER);
// Add Collection type instance creators
map.register(Collection.class, COLLECTION_TYPE_ADAPTER);
map.register(List.class, LINKED_LIST_CREATOR);
map.register(Queue.class, LINKED_LIST_CREATOR);
@ -468,83 +511,192 @@ final class DefaultTypeAdapters {
}
}
private static class LongCreator implements InstanceCreator<Long> {
private static class LongTypeAdapter
implements InstanceCreator<Long>, JsonSerializer<Long>, JsonDeserializer<Long> {
public JsonElement serialize(Long src, Type typeOfSrc, JsonSerializationContext context) {
return new JsonPrimitive(src);
}
public Long deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
return json.getAsLong();
}
public Long createInstance(Type type) {
return new Long(0L);
}
@Override
public String toString() {
return LongCreator.class.getSimpleName();
return LongTypeAdapter.class.getSimpleName();
}
}
private static class IntegerCreator implements InstanceCreator<Integer> {
private static class IntegerTypeAdapter
implements InstanceCreator<Integer>, JsonSerializer<Integer>, JsonDeserializer<Integer> {
public JsonElement serialize(Integer src, Type typeOfSrc, JsonSerializationContext context) {
return new JsonPrimitive(src);
}
public Integer deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
return json.getAsInt();
}
public Integer createInstance(Type type) {
return new Integer(0);
}
@Override
public String toString() {
return IntegerCreator.class.getSimpleName();
return IntegerTypeAdapter.class.getSimpleName();
}
}
private static class ShortCreator implements InstanceCreator<Short> {
private static class ShortTypeAdapter
implements InstanceCreator<Short>, JsonSerializer<Short>, JsonDeserializer<Short> {
public JsonElement serialize(Short src, Type typeOfSrc, JsonSerializationContext context) {
return new JsonPrimitive(src);
}
public Short deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
return json.getAsShort();
}
public Short createInstance(Type type) {
return new Short((short) 0);
}
@Override
public String toString() {
return ShortCreator.class.getSimpleName();
return ShortTypeAdapter.class.getSimpleName();
}
}
private static class ByteCreator implements InstanceCreator<Byte> {
private static class ByteTypeAdapter
implements InstanceCreator<Byte>, JsonSerializer<Byte>, JsonDeserializer<Byte> {
public JsonElement serialize(Byte src, Type typeOfSrc, JsonSerializationContext context) {
return new JsonPrimitive(src);
}
public Byte deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
return json.getAsByte();
}
public Byte createInstance(Type type) {
return new Byte((byte) 0);
}
@Override
public String toString() {
return ByteCreator.class.getSimpleName();
return ByteTypeAdapter.class.getSimpleName();
}
}
private static class FloatCreator implements InstanceCreator<Float> {
private static class FloatTypeAdapter
implements InstanceCreator<Float>, JsonSerializer<Float>, JsonDeserializer<Float> {
public JsonElement serialize(Float src, Type typeOfSrc, JsonSerializationContext context) {
return new JsonPrimitive(src);
}
public Float deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
return json.getAsFloat();
}
public Float createInstance(Type type) {
return new Float(0F);
}
@Override
public String toString() {
return FloatCreator.class.getSimpleName();
return FloatTypeAdapter.class.getSimpleName();
}
}
private static class DoubleCreator implements InstanceCreator<Double> {
private static class DoubleTypeAdapter
implements InstanceCreator<Double>, JsonSerializer<Double>, JsonDeserializer<Double> {
public JsonElement serialize(Double src, Type typeOfSrc, JsonSerializationContext context) {
return new JsonPrimitive(src);
}
public Double deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
return json.getAsDouble();
}
public Double createInstance(Type type) {
return new Double(0D);
}
@Override
public String toString() {
return DoubleCreator.class.getSimpleName();
return DoubleTypeAdapter.class.getSimpleName();
}
}
private static class CharacterCreator implements InstanceCreator<Character> {
private static class CharacterTypeAdapter implements InstanceCreator<Character>,
JsonSerializer<Character>, JsonDeserializer<Character> {
public JsonElement serialize(Character src, Type typeOfSrc, JsonSerializationContext context) {
return new JsonPrimitive(src);
}
public Character deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
return json.getAsCharacter();
}
public Character createInstance(Type type) {
return new Character((char) 0);
return new Character('0');
}
@Override
public String toString() {
return CharacterCreator.class.getSimpleName();
return CharacterTypeAdapter.class.getSimpleName();
}
}
private static class StringTypeAdapter
implements InstanceCreator<String>, JsonSerializer<String>, JsonDeserializer<String> {
public JsonElement serialize(String src, Type typeOfSrc, JsonSerializationContext context) {
return new JsonPrimitive(src);
}
public String deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
return json.getAsString();
}
public String createInstance(Type type) {
return "";
}
@Override
public String toString() {
return StringTypeAdapter.class.getSimpleName();
}
}
private static class BooleanCreator implements InstanceCreator<Boolean> {
private static class BooleanTypeAdapter
implements InstanceCreator<Boolean>, JsonSerializer<Boolean>, JsonDeserializer<Boolean> {
public JsonElement serialize(Boolean src, Type typeOfSrc, JsonSerializationContext context) {
return new JsonPrimitive(src);
}
public Boolean deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
return json.getAsBoolean();
}
public Boolean createInstance(Type type) {
return new Boolean(false);
}
@Override
public String toString() {
return BooleanCreator.class.getSimpleName();
return BooleanTypeAdapter.class.getSimpleName();
}
}

View File

@ -225,6 +225,22 @@ public final class JsonArray extends JsonElement implements Iterable<JsonElement
}
throw new IllegalStateException();
}
@Override
public byte getAsByte() {
if (elements.size() == 1) {
return elements.get(0).getAsByte();
}
throw new IllegalStateException();
}
@Override
public char getAsCharacter() {
if (elements.size() == 1) {
return elements.get(0).getAsCharacter();
}
throw new IllegalStateException();
}
/**
* convenience method to get this array as a primitive short if it contains a single element.

View File

@ -28,14 +28,12 @@ import java.lang.reflect.Type;
* @author Joel Leitch
*/
final class JsonArrayDeserializationVisitor<T> extends JsonDeserializationVisitor<T> {
private final Class<?> componentType;
JsonArrayDeserializationVisitor(JsonArray jsonArray, Type arrayType,
ObjectNavigatorFactory factory, ObjectConstructor objectConstructor,
TypeAdapter typeAdapter, ParameterizedTypeHandlerMap<JsonDeserializer<?>> deserializers,
JsonDeserializationContext context) {
super(jsonArray, arrayType, factory, objectConstructor, typeAdapter, deserializers, context);
this.componentType = TypeUtils.toRawClass(arrayType);
}
@Override
@ -45,13 +43,7 @@ final class JsonArrayDeserializationVisitor<T> extends JsonDeserializationVisito
TypeInfo typeInfo = new TypeInfo(targetType);
JsonArray jsonArray = json.getAsJsonArray();
if (typeInfo.isPrimitiveOrStringAndNotAnArray()) {
if (jsonArray.size() != 1) {
throw new IllegalArgumentException(
"Primitives should be an array of length 1, but was: " + jsonArray);
}
return (T) objectConstructor.construct(typeInfo.getWrappedClass());
} else if (typeInfo.isArray()) {
if (typeInfo.isArray()) {
TypeInfoArray arrayTypeInfo = TypeInfoFactory.getTypeInfoForArray(targetType);
// We know that we are getting back an array of the required type, so
// this typecasting is safe.
@ -76,7 +68,7 @@ final class JsonArrayDeserializationVisitor<T> extends JsonDeserializationVisito
} else if (jsonChild instanceof JsonArray) {
child = visitChildAsArray(arrayTypeInfo.getSecondLevelType(), jsonChild.getAsJsonArray());
} else if (jsonChild instanceof JsonPrimitive) {
child = visitChildAsPrimitive(arrayTypeInfo.getComponentRawType(),
child = visitChildAsObject(arrayTypeInfo.getComponentRawType(),
jsonChild.getAsJsonPrimitive());
} else {
throw new IllegalStateException();
@ -85,11 +77,6 @@ final class JsonArrayDeserializationVisitor<T> extends JsonDeserializationVisito
}
}
@SuppressWarnings("unchecked")
public void visitPrimitiveValue(Object obj) {
target = (T) typeAdapter.adaptType(json.getAsJsonArray().get(0).getAsObject(), componentType);
}
// We should not implement any other method from Visitor interface since
// all other methods should be invoked on JsonObjectDeserializationVisitor
// instead.
@ -106,10 +93,6 @@ final class JsonArrayDeserializationVisitor<T> extends JsonDeserializationVisito
throw new UnsupportedOperationException();
}
public void visitPrimitiveField(Field f, Type typeOfF, Object obj) {
throw new UnsupportedOperationException();
}
public boolean visitFieldUsingCustomHandler(Field f, Type actualTypeOfField, Object parent) {
throw new UnsupportedOperationException();
}

View File

@ -83,12 +83,11 @@ final class JsonDeserializationContextDefault implements JsonDeserializationCont
@SuppressWarnings("unchecked")
private <T> T fromJsonPrimitive(Type typeOfT, JsonPrimitive json,
JsonDeserializationContext context) throws JsonParseException {
JsonPrimitiveDeserializationVisitor<T> visitor = new JsonPrimitiveDeserializationVisitor<T>(
JsonObjectDeserializationVisitor<T> visitor = new JsonObjectDeserializationVisitor<T>(
json, typeOfT, navigatorFactory, objectConstructor, typeAdapter, deserializers, context);
Object target = visitor.getTarget();
ObjectNavigator on = navigatorFactory.create(target, typeOfT);
ObjectNavigator on = navigatorFactory.create(json.getAsObject(), typeOfT);
on.accept(visitor);
target = visitor.getTarget();
Object target = visitor.getTarget();
if (typeOfT instanceof Class) {
target = typeAdapter.adaptType(target, (Class) typeOfT);
}

View File

@ -87,31 +87,6 @@ abstract class JsonDeserializationVisitor<T> implements ObjectNavigator.Visitor
return visitChild(childType, childVisitor);
}
final Object visitChildAsPrimitive(Type childType, JsonPrimitive jsonChild) {
Preconditions.checkNotNull(jsonChild);
Class<?> childClass;
if (childType instanceof Class) {
childClass = (Class<?>) childType;
} else {
childClass = TypeUtils.toRawClass(childType);
}
return typeAdapter.adaptType(jsonChild.getAsObject(), childClass);
}
final Object visitChild(Type childType, JsonElement jsonChild) {
if (jsonChild == null) {
return null;
} else if (jsonChild instanceof JsonArray) {
return visitChildAsArray(childType, jsonChild.getAsJsonArray());
} else if (jsonChild instanceof JsonObject) {
return visitChildAsObject(childType, jsonChild);
} else if (jsonChild instanceof JsonPrimitive) {
return visitChildAsPrimitive(childType, jsonChild.getAsJsonPrimitive());
} else {
throw new IllegalStateException();
}
}
private Object visitChild(Type type, JsonDeserializationVisitor<?> childVisitor) {
Object child = childVisitor.getTarget();
ObjectNavigator on = factory.create(child, type);

View File

@ -221,6 +221,32 @@ public abstract class JsonElement {
public int getAsInt() {
throw new UnsupportedOperationException();
}
/**
* convenience method to get this element as a primitive byte value.
*
* @return get this element as a primitive byte value.
* @throws ClassCastException if the element is of not a {@link JsonPrimitive} and is not a valid
* byte value.
* @throws IllegalStateException if the element is of the type {@link JsonArray} but contains
* more than a single element.
*/
public byte getAsByte() {
throw new UnsupportedOperationException();
}
/**
* convenience method to get this element as a primitive character value.
*
* @return get this element as a primitive char value.
* @throws ClassCastException if the element is of not a {@link JsonPrimitive} and is not a valid
* char value.
* @throws IllegalStateException if the element is of the type {@link JsonArray} but contains
* more than a single element.
*/
public char getAsCharacter() {
throw new UnsupportedOperationException();
}
/**
* convenience method to get this element as a {@link BigDecimal}.

View File

@ -50,11 +50,6 @@ final class JsonObjectDeserializationVisitor<T> extends JsonDeserializationVisit
throw new IllegalStateException();
}
public void visitPrimitiveValue(Object obj) {
// should not be called since this case should invoke JsonPrimitiveDeserializationVisitor
throw new IllegalStateException();
}
public void visitObjectField(Field f, Type typeOfF, Object obj) {
try {
JsonObject jsonObject = json.getAsJsonObject();
@ -87,25 +82,6 @@ final class JsonObjectDeserializationVisitor<T> extends JsonDeserializationVisit
}
}
public void visitPrimitiveField(Field f, Type typeOfF, Object obj) {
try {
JsonObject jsonObject = json.getAsJsonObject();
String fName = getFieldName(f);
JsonPrimitive value = jsonObject.getAsJsonPrimitive(fName);
if (value != null) {
f.set(obj, typeAdapter.adaptType(value.getAsObject(), TypeUtils.toRawClass(typeOfF)));
} else {
// For Strings, we need to set the field to null
// For other primitive types, any value created during default construction is fine
if (f.getType() == String.class) {
f.set(obj, null);
}
}
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
private String getFieldName(Field f) {
FieldNamingStrategy namingPolicy = factory.getFieldNamingPolicy();
return namingPolicy.translateName(f);

View File

@ -255,6 +255,16 @@ public final class JsonPrimitive extends JsonElement {
public int getAsInt() {
return ((Number) value).intValue();
}
@Override
public byte getAsByte() {
return ((Number) value).byteValue();
}
@Override
public char getAsCharacter() {
return getAsString().charAt(0);
}
/**
* convenience method to get this element as an Object.

View File

@ -1,84 +0,0 @@
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.gson;
import java.lang.reflect.Field;
import java.lang.reflect.Type;
/**
* A visitor that populates a primitive value from its JSON representation
*
* @author Inderjeet Singh
*/
final class JsonPrimitiveDeserializationVisitor<T> extends JsonDeserializationVisitor<T> {
JsonPrimitiveDeserializationVisitor(JsonPrimitive json, Type type,
ObjectNavigatorFactory factory, ObjectConstructor objectConstructor,
TypeAdapter typeAdapter, ParameterizedTypeHandlerMap<JsonDeserializer<?>> deserializers,
JsonDeserializationContext context) {
super(json, type, factory, objectConstructor, typeAdapter, deserializers, context);
}
@Override
@SuppressWarnings("unchecked")
protected T constructTarget() {
return (T) objectConstructor.construct(targetType);
}
public void startVisitingObject(Object node) {
// do nothing
}
public void visitArray(Object array, Type componentType) {
// should not be called since this case should invoke JsonArrayDeserializationVisitor
throw new IllegalStateException();
}
@SuppressWarnings("unchecked")
public void visitPrimitiveValue(Object obj) {
JsonPrimitive jsonPrimitive = json.getAsJsonPrimitive();
if (jsonPrimitive.isBoolean()) {
target = (T) jsonPrimitive.getAsBooleanWrapper();
} else if (jsonPrimitive.isNumber()) {
target = (T) jsonPrimitive.getAsNumber();
} else if (jsonPrimitive.isString()) {
target = (T) jsonPrimitive.getAsString();
} else {
throw new IllegalStateException();
}
}
public void visitObjectField(Field f, Type typeOfF, Object obj) {
// should not be called since this case should invoke JsonArrayDeserializationVisitor
throw new IllegalStateException();
}
public void visitArrayField(Field f, Type typeOfF, Object obj) {
// should not be called since this case should invoke JsonArrayDeserializationVisitor
throw new IllegalStateException();
}
public void visitPrimitiveField(Field f, Type fType, Object obj) {
// 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();
}
}

View File

@ -118,26 +118,6 @@ final class JsonSerializationVisitor implements ObjectNavigator.Visitor {
return childVisitor.getJsonElement();
}
public void visitPrimitiveField(Field f, Type typeOfF, Object obj) {
if (isFieldNull(f, obj)) {
if (serializeNulls) {
addChildAsElement(f, JsonNull.INSTANCE);
}
} else {
TypeInfo typeInfo = new TypeInfo(typeOfF);
if (typeInfo.isPrimitiveOrStringAndNotAnArray()) {
Object fieldValue = getFieldValue(f, obj);
addAsChildOfObject(f, typeOfF, fieldValue);
} else {
throw new IllegalArgumentException("Not a primitive type");
}
}
}
public void visitPrimitiveValue(Object obj) {
assignToRoot(new JsonPrimitive(obj));
}
@SuppressWarnings("unchecked")
public boolean visitUsingCustomHandler(Object obj, Type objType) {
JsonSerializer serializer = serializers.getHandlerFor(objType);

View File

@ -39,11 +39,6 @@ final class ObjectNavigator {
*/
void visitArray(Object array, Type componentType);
/**
* This is called to visit the current object if it is a primitive
*/
void visitPrimitiveValue(Object obj);
/**
* This is called to visit an object field of the current object
*/
@ -54,11 +49,6 @@ final class ObjectNavigator {
*/
void visitArrayField(Field f, Type typeOfF, Object obj);
/**
* This is called to visit a primitive field of the current object
*/
void visitPrimitiveField(Field f, Type typeOfF, Object obj);
/**
* This is called to visit an object using a custom handler
* @return true if a custom handler exists, false otherwise
@ -116,8 +106,6 @@ final class ObjectNavigator {
if (!visitedWithCustomHandler) {
if (objTypeInfo.isArray()) {
visitor.visitArray(obj, objType);
} else if (objTypeInfo.isPrimitiveOrStringAndNotAnArray()) {
visitor.visitPrimitiveValue(obj);
} else {
visitor.startVisitingObject(obj);
// For all classes in the inheritance hierarchy (including the current class),
@ -149,8 +137,6 @@ final class ObjectNavigator {
if (!visitedWithCustomHandler) {
if (fieldTypeInfo.isArray()) {
visitor.visitArrayField(f, actualTypeOfField, obj);
} else if (fieldTypeInfo.isPrimitiveOrStringAndNotAnArray()) {
visitor.visitPrimitiveField(f, actualTypeOfField, obj);
} else {
visitor.visitObjectField(f, actualTypeOfField, obj);
}

View File

@ -73,12 +73,4 @@ class TypeInfo {
public final boolean isPrimitive() {
return Primitives.isWrapperType(Primitives.wrap(rawClass));
}
public final boolean isString() {
return rawClass == String.class;
}
public final boolean isPrimitiveOrStringAndNotAnArray() {
return (isPrimitive() || isString()) && !isArray();
}
}

View File

@ -80,8 +80,8 @@ public class TypeInfoArrayTest extends TestCase {
TypeInfoArray arrayTypeInfo = new TypeInfoArray(int[].class);
assertTrue(arrayTypeInfo.isArray());
assertFalse(arrayTypeInfo.isPrimitive());
assertEquals(int.class, arrayTypeInfo.getSecondLevelType());
assertFalse(arrayTypeInfo.isPrimitiveOrStringAndNotAnArray());
}
public void testStringArray() throws Exception {

View File

@ -35,7 +35,6 @@ public class TypeInfoTest extends TestCase {
TypeInfo typeInfo = new TypeInfo(boolean.class);
assertFalse(typeInfo.isArray());
assertFalse(typeInfo.isString());
assertTrue(typeInfo.isPrimitive());
assertEquals(boolean.class, typeInfo.getRawClass());
assertEquals(Boolean.class, typeInfo.getWrappedClass());
@ -46,7 +45,7 @@ public class TypeInfoTest extends TestCase {
assertEquals(Integer.class, typeInfo.getRawClass());
assertTrue(typeInfo.isPrimitive());
assertTrue(typeInfo.isPrimitiveOrStringAndNotAnArray());
assertFalse(typeInfo.isArray());
}
public void testString() throws Exception {
@ -55,7 +54,6 @@ public class TypeInfoTest extends TestCase {
assertFalse(typeInfo.isArray());
assertFalse(typeInfo.isPrimitive());
assertEquals(String.class, typeInfo.getRawClass());
assertTrue(typeInfo.isPrimitiveOrStringAndNotAnArray());
}
public void testObject() throws Exception {
@ -64,7 +62,6 @@ public class TypeInfoTest extends TestCase {
assertFalse(typeInfo.isArray());
assertFalse(typeInfo.isPrimitive());
assertEquals(Object.class, typeInfo.getRawClass());
assertFalse(typeInfo.isPrimitiveOrStringAndNotAnArray());
}
public void testPrimitiveType() throws Exception {
@ -76,7 +73,7 @@ public class TypeInfoTest extends TestCase {
public void testObjectType() throws Exception {
TypeInfo typeInfo = new TypeInfo(String.class);
assertFalse(typeInfo.isArray());
assertTrue(typeInfo.isString());
assertFalse(typeInfo.isPrimitive());
assertEquals(String.class, typeInfo.getRawClass());
}

View File

@ -16,6 +16,15 @@
package com.google.gson.common;
import java.lang.reflect.Type;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import com.google.gson.annotations.SerializedName;
/**
@ -349,4 +358,17 @@ public class TestTypes {
return '{' + "\"fooBar\":" + f + '}';
}
}
public static class CrazyLongTypeAdapter
implements JsonSerializer<Long>, JsonDeserializer<Long> {
public static final long DIFFERENCE = 5L;
public JsonElement serialize(Long src, Type typeOfSrc, JsonSerializationContext context) {
return new JsonPrimitive(src + DIFFERENCE);
}
public Long deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
return json.getAsLong() - DIFFERENCE;
}
}
}

View File

@ -16,7 +16,9 @@
package com.google.gson.functional;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.common.MoreAsserts;
import com.google.gson.common.TestTypes.CrazyLongTypeAdapter;
import com.google.gson.common.TestTypes.MyEnum;
import com.google.gson.reflect.TypeToken;
@ -133,4 +135,20 @@ public class ArrayTest extends TestCase {
MoreAsserts.assertEquals(new Integer[] { 1, 2 }, target[0].toArray(new Integer[0]));
MoreAsserts.assertEquals(new Integer[] { 3, 4 }, target[1].toArray(new Integer[0]));
}
public void testArrayOfPrimitivesWithCustomTypeAdapter() throws Exception {
CrazyLongTypeAdapter typeAdapter = new CrazyLongTypeAdapter();
gson = new GsonBuilder()
.registerTypeAdapter(long.class, typeAdapter)
.registerTypeAdapter(Long.class, typeAdapter)
.create();
long[] value = { 1L };
String serializedValue = gson.toJson(value);
String expected = "[" + String.valueOf(value[0] + CrazyLongTypeAdapter.DIFFERENCE) + "]";
assertEquals(expected, serializedValue);
long[] deserializedValue = gson.fromJson(serializedValue, long[].class);
assertEquals(1, deserializedValue.length);
assertEquals(value[0], deserializedValue[0]);
}
}

View File

@ -334,13 +334,25 @@ public class CustomTypeAdaptersTest extends TestCase {
return new JsonPrimitive(contents);
}
}
// Test created from Issue 70
public void testCustomAdapterInvokedForCollectionElementSerializationWithType() {
Gson gson = new GsonBuilder()
.registerTypeAdapter(StringHolder.class, new StringHolderTypeAdapter())
.create();
Type setType = new TypeToken<Set<StringHolder>>() {}.getType();
StringHolder holder = new StringHolder("Jacob", "Tomaw");
Set<StringHolder> setOfHolders = new HashSet<StringHolder>();
setOfHolders.add(holder);
String json = gson.toJson(setOfHolders, setType);
assertTrue(json.contains("Jacob:Tomaw"));
}
// Test created from Issue 70
public void testCustomAdapterInvokedForCollectionElementSerialization() {
Gson gson = new GsonBuilder()
.registerTypeAdapter(StringHolder.class, new StringHolderTypeAdapter())
.create();
Type setType = new TypeToken<Set<StringHolder>>() {}.getType();
StringHolder holder = new StringHolder("Jacob", "Tomaw");
Set<StringHolder> setOfHolders = new HashSet<StringHolder>();
setOfHolders.add(holder);
@ -362,7 +374,7 @@ public class CustomTypeAdaptersTest extends TestCase {
}
// Test created from Issue 70
public void testCustomAdapterInvokedForMapElementSerialization() {
public void testCustomAdapterInvokedForMapElementSerializationWithType() {
Gson gson = new GsonBuilder()
.registerTypeAdapter(StringHolder.class, new StringHolderTypeAdapter())
.create();
@ -370,6 +382,18 @@ public class CustomTypeAdaptersTest extends TestCase {
StringHolder holder = new StringHolder("Jacob", "Tomaw");
Map<String, StringHolder> mapOfHolders = new HashMap<String, StringHolder>();
mapOfHolders.put("foo", holder);
String json = gson.toJson(mapOfHolders, mapType);
assertTrue(json.contains("\"foo\":\"Jacob:Tomaw\""));
}
// Test created from Issue 70
public void testCustomAdapterInvokedForMapElementSerialization() {
Gson gson = new GsonBuilder()
.registerTypeAdapter(StringHolder.class, new StringHolderTypeAdapter())
.create();
StringHolder holder = new StringHolder("Jacob", "Tomaw");
Map<String, StringHolder> mapOfHolders = new HashMap<String, StringHolder>();
mapOfHolders.put("foo", holder);
String json = gson.toJson(mapOfHolders);
assertTrue(json.contains("\"foo\":\"Jacob:Tomaw\""));
}

View File

@ -271,7 +271,6 @@ public class ObjectTest extends TestCase {
public void testSubInterfacesOfCollectionDeserialization() throws Exception {
String json = "{\"list\":[0,1,2,3],\"queue\":[0,1,2,3],\"set\":[0.1,0.2,0.3,0.4],"
+ "\"sortedSet\":[\"a\",\"b\",\"c\",\"d\"]"
// + ",\"navigableSet\":[\"abc\",\"def\",\"ghi\",\"jkl\"]"
+ "}";
ClassWithSubInterfacesOfCollection target = gson.fromJson(
json, ClassWithSubInterfacesOfCollection.class);

View File

@ -67,7 +67,7 @@ public class ParameterizedTypesTest extends TestCase {
.create();
String json = expected.getExpectedJson();
MyParameterizedType<Integer> actual = gson.fromJson(json, expectedType);
MyParameterizedType<BagOfPrimitives> actual = gson.fromJson(json, expectedType);
assertEquals(expected, actual);
}

View File

@ -16,13 +16,15 @@
package com.google.gson.functional;
import com.google.gson.Gson;
import com.google.gson.JsonParseException;
import java.math.BigDecimal;
import java.math.BigInteger;
import junit.framework.TestCase;
import java.math.BigDecimal;
import java.math.BigInteger;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonParseException;
import com.google.gson.common.TestTypes.CrazyLongTypeAdapter;
/**
* Functional tests for Json primitive values: integers, and floating point numbers.
@ -257,6 +259,20 @@ public class PrimitiveTest extends TestCase {
fail("BigInteger can not be decimal values.");
} catch (JsonParseException expected) { }
}
public void testOverridingDefaultPrimitiveSerialization() {
CrazyLongTypeAdapter typeAdapter = new CrazyLongTypeAdapter();
gson = new GsonBuilder()
.registerTypeAdapter(long.class, typeAdapter)
.registerTypeAdapter(Long.class, typeAdapter)
.create();
long value = 1L;
String serializedValue = gson.toJson(value);
assertEquals(String.valueOf(value + CrazyLongTypeAdapter.DIFFERENCE), serializedValue);
long deserializedValue = gson.fromJson(serializedValue, long.class);
assertEquals(value, deserializedValue);
}
private String extractElementFromArray(String json) {
return json.substring(json.indexOf('[') + 1, json.indexOf(']'));