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:
parent
cc90a68241
commit
eaa43b76e4
@ -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> {
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
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);
|
||||
|
46
gson/src/main/java/com/google/gson/TypeInfoCollection.java
Normal file
46
gson/src/main/java/com/google/gson/TypeInfoCollection.java
Normal 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];
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user