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 UrlTypeAdapter URL_TYPE_ADAPTER = new UrlTypeAdapter();
|
||||||
private static final UriTypeAdapter URI_TYPE_ADAPTER = new UriTypeAdapter();
|
private static final UriTypeAdapter URI_TYPE_ADAPTER = new UriTypeAdapter();
|
||||||
private static final LocaleTypeAdapter LOCALE_TYPE_ADAPTER = new LocaleTypeAdapter();
|
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 MapTypeAdapter MAP_TYPE_ADAPTER = new MapTypeAdapter();
|
||||||
private static final BigDecimalTypeAdapter BIG_DECIMAL_TYPE_ADAPTER = new BigDecimalTypeAdapter();
|
private static final BigDecimalTypeAdapter BIG_DECIMAL_TYPE_ADAPTER = new BigDecimalTypeAdapter();
|
||||||
private static final BigIntegerTypeAdapter BIG_INTEGER_TYPE_ADAPTER = new BigIntegerTypeAdapter();
|
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(URL.class, wrapSerializer(URL_TYPE_ADAPTER));
|
||||||
map.register(URI.class, wrapSerializer(URI_TYPE_ADAPTER));
|
map.register(URI.class, wrapSerializer(URI_TYPE_ADAPTER));
|
||||||
map.register(Locale.class, wrapSerializer(LOCALE_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(Map.class, wrapSerializer(MAP_TYPE_ADAPTER));
|
||||||
map.register(Date.class, wrapSerializer(DATE_TYPE_ADAPTER));
|
map.register(Date.class, wrapSerializer(DATE_TYPE_ADAPTER));
|
||||||
map.register(BigDecimal.class, wrapSerializer(BIG_DECIMAL_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(URL.class, wrapDeserializer(URL_TYPE_ADAPTER));
|
||||||
map.register(URI.class, wrapDeserializer(URI_TYPE_ADAPTER));
|
map.register(URI.class, wrapDeserializer(URI_TYPE_ADAPTER));
|
||||||
map.register(Locale.class, wrapDeserializer(LOCALE_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(Map.class, wrapDeserializer(MAP_TYPE_ADAPTER));
|
||||||
map.register(Date.class, wrapDeserializer(DATE_TYPE_ADAPTER));
|
map.register(Date.class, wrapDeserializer(DATE_TYPE_ADAPTER));
|
||||||
map.register(BigDecimal.class, wrapDeserializer(BIG_DECIMAL_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(Enum.class, ENUM_TYPE_ADAPTER);
|
||||||
map.register(URL.class, URL_TYPE_ADAPTER);
|
map.register(URL.class, URL_TYPE_ADAPTER);
|
||||||
map.register(Locale.class, LOCALE_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(Map.class, MAP_TYPE_ADAPTER);
|
||||||
map.register(BigDecimal.class, BIG_DECIMAL_TYPE_ADAPTER);
|
map.register(BigDecimal.class, BIG_DECIMAL_TYPE_ADAPTER);
|
||||||
map.register(BigInteger.class, BIG_INTEGER_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")
|
@SuppressWarnings("unchecked")
|
||||||
static class MapTypeAdapter implements JsonSerializer<Map>, JsonDeserializer<Map>,
|
static class MapTypeAdapter implements JsonSerializer<Map>, JsonDeserializer<Map>,
|
||||||
InstanceCreator<Map> {
|
InstanceCreator<Map> {
|
||||||
|
@ -17,6 +17,8 @@
|
|||||||
package com.google.gson;
|
package com.google.gson;
|
||||||
|
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -78,9 +80,16 @@ abstract class JsonDeserializationVisitor<T> implements ObjectNavigator.Visitor
|
|||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public final boolean visitUsingCustomHandler(Object obj, Type objType) {
|
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) {
|
if (deserializer != null) {
|
||||||
target = deserializer.deserialize(json, objType, context);
|
target = (T) deserializer.deserialize(json, objType, context);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -187,8 +187,12 @@ final class JsonSerializationVisitor implements ObjectNavigator.Visitor {
|
|||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public boolean visitUsingCustomHandler(Object obj, Type objType) {
|
public boolean visitUsingCustomHandler(Object obj, Type objType) {
|
||||||
JsonSerializer serializer = serializers.getHandlerFor(objType);
|
JsonSerializer serializer = serializers.getHandlerFor(objType);
|
||||||
if (serializer == null && obj instanceof Map) {
|
if (serializer == null) {
|
||||||
serializer = serializers.getHandlerFor(Map.class);
|
if (obj instanceof Map) {
|
||||||
|
serializer = serializers.getHandlerFor(Map.class);
|
||||||
|
} else if (obj instanceof Collection) {
|
||||||
|
serializer = serializers.getHandlerFor(Collection.class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (serializer != null) {
|
if (serializer != null) {
|
||||||
if (obj == null) {
|
if (obj == null) {
|
||||||
@ -210,6 +214,9 @@ final class JsonSerializationVisitor implements ObjectNavigator.Visitor {
|
|||||||
if (serializer == null && obj instanceof Map) {
|
if (serializer == null && obj instanceof Map) {
|
||||||
serializer = serializers.getHandlerFor(Map.class);
|
serializer = serializers.getHandlerFor(Map.class);
|
||||||
}
|
}
|
||||||
|
if (serializer == null && obj instanceof Collection) {
|
||||||
|
serializer = serializers.getHandlerFor(Collection.class);
|
||||||
|
}
|
||||||
if (serializer != null) {
|
if (serializer != null) {
|
||||||
JsonElement child = serializer.serialize(obj, actualTypeOfField, context);
|
JsonElement child = serializer.serialize(obj, actualTypeOfField, context);
|
||||||
addChildAsElement(f, child);
|
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 junit.framework.TestCase;
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
|
import com.google.gson.InstanceCreator;
|
||||||
import com.google.gson.reflect.TypeToken;
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -73,4 +75,27 @@ public class MapTest extends TestCase {
|
|||||||
String json = gson.toJson(map, typeOfMap);
|
String json = gson.toJson(map, typeOfMap);
|
||||||
assertEquals("{}", json);
|
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