From 0d8150fe52eba34a0472c47be71587885ba4a94c Mon Sep 17 00:00:00 2001 From: Joel Leitch Date: Sat, 15 Nov 2008 02:26:57 +0000 Subject: [PATCH] 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). --- .../com/google/gson/DefaultTypeAdapters.java | 240 ++++++++++++++---- .../main/java/com/google/gson/JsonArray.java | 16 ++ .../gson/JsonArrayDeserializationVisitor.java | 21 +- .../JsonDeserializationContextDefault.java | 7 +- .../gson/JsonDeserializationVisitor.java | 25 -- .../java/com/google/gson/JsonElement.java | 26 ++ .../JsonObjectDeserializationVisitor.java | 24 -- .../java/com/google/gson/JsonPrimitive.java | 10 + .../JsonPrimitiveDeserializationVisitor.java | 84 ------ .../google/gson/JsonSerializationVisitor.java | 20 -- .../java/com/google/gson/ObjectNavigator.java | 14 - .../main/java/com/google/gson/TypeInfo.java | 8 - .../com/google/gson/TypeInfoArrayTest.java | 2 +- .../java/com/google/gson/TypeInfoTest.java | 7 +- .../com/google/gson/common/TestTypes.java | 22 ++ .../com/google/gson/functional/ArrayTest.java | 18 ++ .../functional/CustomTypeAdaptersTest.java | 28 +- .../google/gson/functional/ObjectTest.java | 1 - .../functional/ParameterizedTypesTest.java | 2 +- .../google/gson/functional/PrimitiveTest.java | 24 +- 20 files changed, 343 insertions(+), 256 deletions(-) delete mode 100644 gson/src/main/java/com/google/gson/JsonPrimitiveDeserializationVisitor.java diff --git a/gson/src/main/java/com/google/gson/DefaultTypeAdapters.java b/gson/src/main/java/com/google/gson/DefaultTypeAdapters.java index dd2f2136..53435c09 100644 --- a/gson/src/main/java/com/google/gson/DefaultTypeAdapters.java +++ b/gson/src/main/java/com/google/gson/DefaultTypeAdapters.java @@ -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 { + private static class LongTypeAdapter + implements InstanceCreator, JsonSerializer, JsonDeserializer { + 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 { + private static class IntegerTypeAdapter + implements InstanceCreator, JsonSerializer, JsonDeserializer { + 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 { + private static class ShortTypeAdapter + implements InstanceCreator, JsonSerializer, JsonDeserializer { + 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 { + private static class ByteTypeAdapter + implements InstanceCreator, JsonSerializer, JsonDeserializer { + 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 { + private static class FloatTypeAdapter + implements InstanceCreator, JsonSerializer, JsonDeserializer { + 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 { + private static class DoubleTypeAdapter + implements InstanceCreator, JsonSerializer, JsonDeserializer { + 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 { + private static class CharacterTypeAdapter implements InstanceCreator, + JsonSerializer, JsonDeserializer { + 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, JsonSerializer, JsonDeserializer { + 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 { + private static class BooleanTypeAdapter + implements InstanceCreator, JsonSerializer, JsonDeserializer { + 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(); } } diff --git a/gson/src/main/java/com/google/gson/JsonArray.java b/gson/src/main/java/com/google/gson/JsonArray.java index d5bdbdfb..c97c3e3e 100644 --- a/gson/src/main/java/com/google/gson/JsonArray.java +++ b/gson/src/main/java/com/google/gson/JsonArray.java @@ -225,6 +225,22 @@ public final class JsonArray extends JsonElement implements Iterable extends JsonDeserializationVisitor { - private final Class componentType; JsonArrayDeserializationVisitor(JsonArray jsonArray, Type arrayType, ObjectNavigatorFactory factory, ObjectConstructor objectConstructor, TypeAdapter typeAdapter, ParameterizedTypeHandlerMap> deserializers, JsonDeserializationContext context) { super(jsonArray, arrayType, factory, objectConstructor, typeAdapter, deserializers, context); - this.componentType = TypeUtils.toRawClass(arrayType); } @Override @@ -45,13 +43,7 @@ final class JsonArrayDeserializationVisitor 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 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 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 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(); } diff --git a/gson/src/main/java/com/google/gson/JsonDeserializationContextDefault.java b/gson/src/main/java/com/google/gson/JsonDeserializationContextDefault.java index 4e0d9668..b47c0860 100644 --- a/gson/src/main/java/com/google/gson/JsonDeserializationContextDefault.java +++ b/gson/src/main/java/com/google/gson/JsonDeserializationContextDefault.java @@ -83,12 +83,11 @@ final class JsonDeserializationContextDefault implements JsonDeserializationCont @SuppressWarnings("unchecked") private T fromJsonPrimitive(Type typeOfT, JsonPrimitive json, JsonDeserializationContext context) throws JsonParseException { - JsonPrimitiveDeserializationVisitor visitor = new JsonPrimitiveDeserializationVisitor( + JsonObjectDeserializationVisitor visitor = new JsonObjectDeserializationVisitor( 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); } diff --git a/gson/src/main/java/com/google/gson/JsonDeserializationVisitor.java b/gson/src/main/java/com/google/gson/JsonDeserializationVisitor.java index 25ef72f5..522f1710 100644 --- a/gson/src/main/java/com/google/gson/JsonDeserializationVisitor.java +++ b/gson/src/main/java/com/google/gson/JsonDeserializationVisitor.java @@ -87,31 +87,6 @@ abstract class JsonDeserializationVisitor 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); diff --git a/gson/src/main/java/com/google/gson/JsonElement.java b/gson/src/main/java/com/google/gson/JsonElement.java index 6a960e83..e027fd29 100644 --- a/gson/src/main/java/com/google/gson/JsonElement.java +++ b/gson/src/main/java/com/google/gson/JsonElement.java @@ -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}. diff --git a/gson/src/main/java/com/google/gson/JsonObjectDeserializationVisitor.java b/gson/src/main/java/com/google/gson/JsonObjectDeserializationVisitor.java index 8dfd9804..2df6abf7 100644 --- a/gson/src/main/java/com/google/gson/JsonObjectDeserializationVisitor.java +++ b/gson/src/main/java/com/google/gson/JsonObjectDeserializationVisitor.java @@ -50,11 +50,6 @@ final class JsonObjectDeserializationVisitor 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 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); diff --git a/gson/src/main/java/com/google/gson/JsonPrimitive.java b/gson/src/main/java/com/google/gson/JsonPrimitive.java index b968e571..d696bc37 100644 --- a/gson/src/main/java/com/google/gson/JsonPrimitive.java +++ b/gson/src/main/java/com/google/gson/JsonPrimitive.java @@ -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. diff --git a/gson/src/main/java/com/google/gson/JsonPrimitiveDeserializationVisitor.java b/gson/src/main/java/com/google/gson/JsonPrimitiveDeserializationVisitor.java deleted file mode 100644 index 92026b28..00000000 --- a/gson/src/main/java/com/google/gson/JsonPrimitiveDeserializationVisitor.java +++ /dev/null @@ -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 extends JsonDeserializationVisitor { - - JsonPrimitiveDeserializationVisitor(JsonPrimitive json, Type type, - ObjectNavigatorFactory factory, ObjectConstructor objectConstructor, - TypeAdapter typeAdapter, ParameterizedTypeHandlerMap> 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(); - } -} diff --git a/gson/src/main/java/com/google/gson/JsonSerializationVisitor.java b/gson/src/main/java/com/google/gson/JsonSerializationVisitor.java index 0fae7cc7..1714ac5d 100644 --- a/gson/src/main/java/com/google/gson/JsonSerializationVisitor.java +++ b/gson/src/main/java/com/google/gson/JsonSerializationVisitor.java @@ -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); diff --git a/gson/src/main/java/com/google/gson/ObjectNavigator.java b/gson/src/main/java/com/google/gson/ObjectNavigator.java index 963e59fc..bc394907 100644 --- a/gson/src/main/java/com/google/gson/ObjectNavigator.java +++ b/gson/src/main/java/com/google/gson/ObjectNavigator.java @@ -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); } diff --git a/gson/src/main/java/com/google/gson/TypeInfo.java b/gson/src/main/java/com/google/gson/TypeInfo.java index e156b310..e34b34cd 100644 --- a/gson/src/main/java/com/google/gson/TypeInfo.java +++ b/gson/src/main/java/com/google/gson/TypeInfo.java @@ -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(); - } } \ No newline at end of file diff --git a/gson/src/test/java/com/google/gson/TypeInfoArrayTest.java b/gson/src/test/java/com/google/gson/TypeInfoArrayTest.java index 12492c4c..92731f78 100644 --- a/gson/src/test/java/com/google/gson/TypeInfoArrayTest.java +++ b/gson/src/test/java/com/google/gson/TypeInfoArrayTest.java @@ -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 { diff --git a/gson/src/test/java/com/google/gson/TypeInfoTest.java b/gson/src/test/java/com/google/gson/TypeInfoTest.java index 524bb042..7fadd4ec 100644 --- a/gson/src/test/java/com/google/gson/TypeInfoTest.java +++ b/gson/src/test/java/com/google/gson/TypeInfoTest.java @@ -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()); } diff --git a/gson/src/test/java/com/google/gson/common/TestTypes.java b/gson/src/test/java/com/google/gson/common/TestTypes.java index eef00b15..d03b746f 100644 --- a/gson/src/test/java/com/google/gson/common/TestTypes.java +++ b/gson/src/test/java/com/google/gson/common/TestTypes.java @@ -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, JsonDeserializer { + 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; + } +} } \ No newline at end of file diff --git a/gson/src/test/java/com/google/gson/functional/ArrayTest.java b/gson/src/test/java/com/google/gson/functional/ArrayTest.java index e25a77ab..921673db 100644 --- a/gson/src/test/java/com/google/gson/functional/ArrayTest.java +++ b/gson/src/test/java/com/google/gson/functional/ArrayTest.java @@ -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]); + } } diff --git a/gson/src/test/java/com/google/gson/functional/CustomTypeAdaptersTest.java b/gson/src/test/java/com/google/gson/functional/CustomTypeAdaptersTest.java index 4fbbc74c..8d754a8d 100644 --- a/gson/src/test/java/com/google/gson/functional/CustomTypeAdaptersTest.java +++ b/gson/src/test/java/com/google/gson/functional/CustomTypeAdaptersTest.java @@ -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>() {}.getType(); + StringHolder holder = new StringHolder("Jacob", "Tomaw"); + Set setOfHolders = new HashSet(); + 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>() {}.getType(); StringHolder holder = new StringHolder("Jacob", "Tomaw"); Set setOfHolders = new HashSet(); 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 mapOfHolders = new HashMap(); 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 mapOfHolders = new HashMap(); + mapOfHolders.put("foo", holder); String json = gson.toJson(mapOfHolders); assertTrue(json.contains("\"foo\":\"Jacob:Tomaw\"")); } diff --git a/gson/src/test/java/com/google/gson/functional/ObjectTest.java b/gson/src/test/java/com/google/gson/functional/ObjectTest.java index bce9637b..c0fadeed 100644 --- a/gson/src/test/java/com/google/gson/functional/ObjectTest.java +++ b/gson/src/test/java/com/google/gson/functional/ObjectTest.java @@ -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); diff --git a/gson/src/test/java/com/google/gson/functional/ParameterizedTypesTest.java b/gson/src/test/java/com/google/gson/functional/ParameterizedTypesTest.java index 8823c638..534a3539 100644 --- a/gson/src/test/java/com/google/gson/functional/ParameterizedTypesTest.java +++ b/gson/src/test/java/com/google/gson/functional/ParameterizedTypesTest.java @@ -67,7 +67,7 @@ public class ParameterizedTypesTest extends TestCase { .create(); String json = expected.getExpectedJson(); - MyParameterizedType actual = gson.fromJson(json, expectedType); + MyParameterizedType actual = gson.fromJson(json, expectedType); assertEquals(expected, actual); } diff --git a/gson/src/test/java/com/google/gson/functional/PrimitiveTest.java b/gson/src/test/java/com/google/gson/functional/PrimitiveTest.java index 54edebb9..21e00496 100644 --- a/gson/src/test/java/com/google/gson/functional/PrimitiveTest.java +++ b/gson/src/test/java/com/google/gson/functional/PrimitiveTest.java @@ -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(']'));