Added a custom type adapter for Collection class. We will migrate the code to use it instead of special cases for collections all over. This type adapter is called at a few places already.

Also added tests for verifying that Gson can handle serialization and deserialization of sub types of Maps. The deserialization test fails currently.
This commit is contained in:
Inderjeet Singh 2008-11-13 23:40:10 +00:00
parent cc90a68241
commit eaa43b76e4
5 changed files with 136 additions and 4 deletions

View File

@ -59,6 +59,7 @@ final class DefaultTypeAdapters {
private static final UrlTypeAdapter URL_TYPE_ADAPTER = new UrlTypeAdapter();
private static final UriTypeAdapter URI_TYPE_ADAPTER = new UriTypeAdapter();
private static final LocaleTypeAdapter LOCALE_TYPE_ADAPTER = new LocaleTypeAdapter();
private static final CollectionTypeAdapter COLLECTION_TYPE_ADAPTER = new CollectionTypeAdapter();
private static final MapTypeAdapter MAP_TYPE_ADAPTER = new MapTypeAdapter();
private static final BigDecimalTypeAdapter BIG_DECIMAL_TYPE_ADAPTER = new BigDecimalTypeAdapter();
private static final BigIntegerTypeAdapter BIG_INTEGER_TYPE_ADAPTER = new BigIntegerTypeAdapter();
@ -92,6 +93,7 @@ final class DefaultTypeAdapters {
map.register(URL.class, wrapSerializer(URL_TYPE_ADAPTER));
map.register(URI.class, wrapSerializer(URI_TYPE_ADAPTER));
map.register(Locale.class, wrapSerializer(LOCALE_TYPE_ADAPTER));
map.register(Collection.class, COLLECTION_TYPE_ADAPTER);
map.register(Map.class, wrapSerializer(MAP_TYPE_ADAPTER));
map.register(Date.class, wrapSerializer(DATE_TYPE_ADAPTER));
map.register(BigDecimal.class, wrapSerializer(BIG_DECIMAL_TYPE_ADAPTER));
@ -107,6 +109,7 @@ final class DefaultTypeAdapters {
map.register(URL.class, wrapDeserializer(URL_TYPE_ADAPTER));
map.register(URI.class, wrapDeserializer(URI_TYPE_ADAPTER));
map.register(Locale.class, wrapDeserializer(LOCALE_TYPE_ADAPTER));
map.register(Collection.class, wrapDeserializer(COLLECTION_TYPE_ADAPTER));
map.register(Map.class, wrapDeserializer(MAP_TYPE_ADAPTER));
map.register(Date.class, wrapDeserializer(DATE_TYPE_ADAPTER));
map.register(BigDecimal.class, wrapDeserializer(BIG_DECIMAL_TYPE_ADAPTER));
@ -121,6 +124,7 @@ 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);
@ -323,6 +327,47 @@ final class DefaultTypeAdapters {
}
}
@SuppressWarnings({ "unchecked" })
private static class CollectionTypeAdapter implements JsonSerializer<Collection>, JsonDeserializer<Collection>, InstanceCreator<Collection> {
public JsonElement serialize(Collection src, Type typeOfSrc, JsonSerializationContext context) {
if (src == null) {
return JsonNull.INSTANCE;
}
JsonArray array = new JsonArray();
Type childGenericType = null;
if (typeOfSrc instanceof ParameterizedType) {
childGenericType = new TypeInfoCollection(typeOfSrc).getElementType();
}
for (Object child : src) {
Type childType = (childGenericType == null) ?
childType = child.getClass() : childGenericType;
JsonElement element = context.serialize(child, childType);
array.add(element);
}
return array;
}
public Collection deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
if (json.isJsonNull()) {
return null;
}
// Using list to preserve order in which elements are entered
List<Object> list = new LinkedList<Object>();
Type childType = new TypeInfoCollection(typeOfT).getElementType();
for (JsonElement childElement : json.getAsJsonArray()) {
Object value = context.deserialize(childElement, childType);
list.add(value);
}
return list;
}
public Collection createInstance(Type type) {
return new LinkedList();
}
}
@SuppressWarnings("unchecked")
static class MapTypeAdapter implements JsonSerializer<Map>, JsonDeserializer<Map>,
InstanceCreator<Map> {

View File

@ -17,6 +17,8 @@
package com.google.gson;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Map;
import java.util.logging.Logger;
/**
@ -78,9 +80,16 @@ abstract class JsonDeserializationVisitor<T> implements ObjectNavigator.Visitor
@SuppressWarnings("unchecked")
public final boolean visitUsingCustomHandler(Object obj, Type objType) {
JsonDeserializer<T> deserializer = (JsonDeserializer<T>) deserializers.getHandlerFor(objType);
JsonDeserializer deserializer = (JsonDeserializer) deserializers.getHandlerFor(objType);
if (deserializer == null) {
if (objType instanceof Map) {
deserializer = deserializers.getHandlerFor(Map.class);
} else if (objType instanceof Collection) {
deserializer = deserializers.getHandlerFor(Collection.class);
}
}
if (deserializer != null) {
target = deserializer.deserialize(json, objType, context);
target = (T) deserializer.deserialize(json, objType, context);
return true;
}
return false;

View File

@ -187,8 +187,12 @@ final class JsonSerializationVisitor implements ObjectNavigator.Visitor {
@SuppressWarnings("unchecked")
public boolean visitUsingCustomHandler(Object obj, Type objType) {
JsonSerializer serializer = serializers.getHandlerFor(objType);
if (serializer == null && obj instanceof Map) {
serializer = serializers.getHandlerFor(Map.class);
if (serializer == null) {
if (obj instanceof Map) {
serializer = serializers.getHandlerFor(Map.class);
} else if (obj instanceof Collection) {
serializer = serializers.getHandlerFor(Collection.class);
}
}
if (serializer != null) {
if (obj == null) {
@ -210,6 +214,9 @@ final class JsonSerializationVisitor implements ObjectNavigator.Visitor {
if (serializer == null && obj instanceof Map) {
serializer = serializers.getHandlerFor(Map.class);
}
if (serializer == null && obj instanceof Collection) {
serializer = serializers.getHandlerFor(Collection.class);
}
if (serializer != null) {
JsonElement child = serializer.serialize(obj, actualTypeOfField, context);
addChildAsElement(f, child);

View File

@ -0,0 +1,46 @@
/*
* 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.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
/**
* A convenience object for retrieving the map type information.
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
final class TypeInfoCollection {
private final ParameterizedType collectionType;
public TypeInfoCollection(Type collectionType) {
if (!(collectionType instanceof ParameterizedType)) {
throw new IllegalArgumentException(
"Collection objects need to be parameterized unless you use a custom serializer. "
+ "Use the com.google.gson.reflect.TypeToken to extract the ParameterizedType.");
}
TypeInfo rawType = new TypeInfo(collectionType);
Preconditions.checkArgument(Collection.class.isAssignableFrom(rawType.getRawClass()));
this.collectionType = (ParameterizedType) collectionType;
}
public Type getElementType() {
return collectionType.getActualTypeArguments()[0];
}
}

View File

@ -22,6 +22,8 @@ import java.util.Map;
import junit.framework.TestCase;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.InstanceCreator;
import com.google.gson.reflect.TypeToken;
/**
@ -73,4 +75,27 @@ public class MapTest extends TestCase {
String json = gson.toJson(map, typeOfMap);
assertEquals("{}", json);
}
public void testMapSubclassSerialization() {
MyMap map = new MyMap();
map.put("a", "b");
String json = gson.toJson(map);
assertTrue(json.contains("\"a\":\"b\""));
}
public void testMapSubclassDeserialization() {
Gson gson = new GsonBuilder().registerTypeAdapter(MyMap.class, new InstanceCreator<MyMap>(){
public MyMap createInstance(Type type) {
return new MyMap();
}
}).create();
String json = "{\"a\":1,\"b\":2}";
MyMap map = gson.fromJson(json, MyMap.class);
assertEquals("1", map.get("a"));
assertEquals("2", map.get("b"));
}
private static class MyMap extends LinkedHashMap<String, String> {
int foo = 10;
}
}