Adapter for Object.class

This commit is contained in:
Jesse Wilson 2011-09-09 03:31:16 +00:00
parent 9fb39c89ea
commit 3aeb70e030
10 changed files with 286 additions and 12 deletions

View File

@ -28,12 +28,12 @@ import java.util.Map;
abstract class BaseMapTypeAdapter
implements JsonSerializer<Map<?, ?>>, JsonDeserializer<Map<?, ?>> {
protected static final JsonElement serialize(JsonSerializationContext context,
protected static JsonElement serialize(JsonSerializationContext context,
Object src, Type srcType) {
return context.serialize(src, srcType, false, false);
}
protected static final Map<Object, Object> constructMapType(
protected static Map<Object, Object> constructMapType(
Type mapType, JsonDeserializationContext context) {
return context.construct(mapType);
}

View File

@ -21,9 +21,10 @@ import com.google.gson.internal.bind.ArrayTypeAdapter;
import com.google.gson.internal.bind.BigDecimalTypeAdapter;
import com.google.gson.internal.bind.BigIntegerTypeAdapter;
import com.google.gson.internal.bind.CollectionTypeAdapter;
import com.google.gson.internal.bind.GsonCompatibleMapTypeAdapter;
import com.google.gson.internal.bind.MiniGson;
import com.google.gson.internal.bind.ObjectTypeAdapter;
import com.google.gson.internal.bind.ReflectiveTypeAdapter;
import com.google.gson.internal.bind.StringToValueMapTypeAdapter;
import com.google.gson.internal.bind.TypeAdapter;
import com.google.gson.internal.bind.TypeAdapters;
import com.google.gson.reflect.TypeToken;
@ -34,7 +35,6 @@ import com.google.gson.stream.MalformedJsonException;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.reflect.Field;
@ -246,10 +246,11 @@ public final class Gson {
.typeAdapter(BigDecimal.class, new BigDecimalTypeAdapter())
.typeAdapter(BigInteger.class, new BigIntegerTypeAdapter())
.factory(excludedTypeFactory)
.factory(GsonCompatibleMapTypeAdapter.FACTORY)
.factory(new GsonToMiniGsonTypeAdapter(serializers, deserializers, serializeNulls))
.factory(CollectionTypeAdapter.FACTORY)
.factory(StringToValueMapTypeAdapter.FACTORY)
.factory(ArrayTypeAdapter.FACTORY)
.factory(ObjectTypeAdapter.FACTORY)
.factory(reflectiveTypeAdapterFactory);
this.miniGson = builder.build();

View File

@ -68,7 +68,7 @@ final class GsonToMiniGsonTypeAdapter implements TypeAdapter.Factory {
}
};
}
public JsonSerializationContext createSerializationContext(final MiniGson miniGson) {
return new JsonSerializationContext() {
@Override
@ -85,6 +85,18 @@ final class GsonToMiniGsonTypeAdapter implements TypeAdapter.Factory {
TypeToken typeToken = TypeToken.get(typeOfT);
return (T) miniGson.getAdapter(typeToken).fromJsonElement(json);
}
@Override public <T> T construct(Type type) {
throw new UnsupportedOperationException();
}
@Override public Object constructArray(Type type, int length) {
throw new UnsupportedOperationException();
}
@Override public <T> T deserializeDefault(JsonElement json, Type typeOfT) throws JsonParseException {
throw new UnsupportedOperationException();
}
};
}
}

View File

@ -46,7 +46,10 @@ public class JsonDeserializationContext {
this(null, null, null, null);
}
@SuppressWarnings("unchecked")
/**
* TODO: remove this from the public API
*/
@SuppressWarnings("unchecked") @Deprecated
public <T> T construct(Type type) {
Object instance = objectConstructor.construct(type);
return (T) instance;

View File

@ -56,12 +56,12 @@ public final class CollectionTypeAdapter<E> extends TypeAdapter<Collection<E>> {
} else {
constructorType = rawType;
}
// TODO: Queue=LinkedList, SortedSet=TreeSet
Constructor<?> constructor;
Constructor<?> constructor = null;
try {
constructor = constructorType.getConstructor();
} catch (NoSuchMethodException e) {
return null;
} catch (NoSuchMethodException ignored) {
}
@SuppressWarnings("unchecked") // create() doesn't define a type parameter
@ -86,6 +86,9 @@ public final class CollectionTypeAdapter<E> extends TypeAdapter<Collection<E>> {
return null;
}
if (constructor == null) {
throw new UnsupportedOperationException("TODO: use unsafeAllocator.newInstance");
}
Collection<E> collection = Reflection.newInstance(constructor);
reader.beginArray();
while (reader.hasNext()) {

View File

@ -0,0 +1,104 @@
/*
* Copyright (C) 2011 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.internal.bind;
import com.google.gson.internal.$Gson$Types;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Adapt a map whose keys are any type.
*/
public final class GsonCompatibleMapTypeAdapter<V> extends TypeAdapter<Map<String, V>> {
public static final Factory FACTORY = new Factory() {
public <T> TypeAdapter<T> create(MiniGson context, TypeToken<T> typeToken) {
Type type = typeToken.getType();
Class<? super T> rawType = typeToken.getRawType();
if (!Map.class.isAssignableFrom(rawType)) {
return null;
}
Type childGenericType = Object.class;
if (type instanceof ParameterizedType) {
Class<?> rawTypeOfSrc = $Gson$Types.getRawType(type);
childGenericType = $Gson$Types.getMapKeyAndValueTypes(type, rawTypeOfSrc)[1];
}
TypeAdapter valueAdapter = context.getAdapter(TypeToken.get(childGenericType));
Constructor<?> constructor;
try {
Class<?> constructorType = (rawType == Map.class) ? LinkedHashMap.class : rawType;
constructor = constructorType.getConstructor();
} catch (NoSuchMethodException e) {
return null;
}
@SuppressWarnings("unchecked") // we don't define a type parameter for the key or value types
TypeAdapter<T> result = new GsonCompatibleMapTypeAdapter(valueAdapter, constructor);
return result;
}
};
private final TypeAdapter<V> valueTypeAdapter;
private final Constructor<? extends Map<String, V>> constructor;
public GsonCompatibleMapTypeAdapter(TypeAdapter<V> valueTypeAdapter,
Constructor<? extends Map<String, V>> constructor) {
this.valueTypeAdapter = valueTypeAdapter;
this.constructor = constructor;
}
public Map<String, V> read(JsonReader reader) throws IOException {
if (reader.peek() == JsonToken.NULL) {
reader.nextNull(); // TODO: does this belong here?
return null;
}
Map<String, V> map = Reflection.newInstance(constructor);
reader.beginObject();
while (reader.hasNext()) {
String key = reader.nextName();
V value = valueTypeAdapter.read(reader);
map.put(key, value);
}
reader.endObject();
return map;
}
public void write(JsonWriter writer, Map<String, V> map) throws IOException {
if (map == null) {
writer.nullValue(); // TODO: better policy here?
return;
}
writer.beginObject();
for (Map.Entry<String, V> entry : map.entrySet()) {
writer.name(entry.getKey());
valueTypeAdapter.write(writer, entry.getValue());
}
writer.endObject();
}
}

View File

@ -62,6 +62,7 @@ public final class MiniGson {
factories.add(CollectionTypeAdapter.FACTORY);
factories.add(StringToValueMapTypeAdapter.FACTORY);
factories.add(ArrayTypeAdapter.FACTORY);
factories.add(ObjectTypeAdapter.FACTORY);
factories.add(ReflectiveTypeAdapter.FACTORY);
}
this.factories = Collections.unmodifiableList(factories);

View File

@ -0,0 +1,104 @@
/*
* Copyright (C) 2011 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.internal.bind;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* Adapts types whose static type is only 'Object'. Uses getClass() on
* serialization and a primitive/Map/List on deserialization.
*/
public final class ObjectTypeAdapter extends TypeAdapter<Object> {
public static final Factory FACTORY = new Factory() {
@SuppressWarnings("unchecked")
@Override public <T> TypeAdapter<T> create(MiniGson context, TypeToken<T> type) {
if (type.getRawType() == Object.class) {
return (TypeAdapter<T>) new ObjectTypeAdapter(context);
}
return null;
}
};
private final MiniGson miniGson;
private ObjectTypeAdapter(MiniGson miniGson) {
this.miniGson = miniGson;
}
@Override public Object read(JsonReader reader) throws IOException {
JsonToken token = reader.peek();
switch (token) {
case BEGIN_ARRAY:
List<Object> list = new ArrayList<Object>();
reader.beginArray();
while (reader.hasNext()) {
list.add(read(reader));
}
reader.endArray();
return list;
case BEGIN_OBJECT:
Map<String, Object> map = new LinkedHashMap<String, Object>();
reader.beginObject();
while (reader.hasNext()) {
map.put(reader.nextName(), read(reader));
}
reader.endObject();
return map;
case STRING:
return reader.nextString();
case NUMBER:
return reader.nextDouble();
case BOOLEAN:
return reader.nextBoolean();
case NULL:
reader.nextNull();
return null;
}
throw new IllegalStateException();
}
@SuppressWarnings("unchecked")
@Override public void write(JsonWriter writer, Object value) throws IOException {
if (value == null) {
writer.nullValue(); // TODO: better policy here?
return;
}
TypeAdapter<Object> typeAdapter = (TypeAdapter<Object>) miniGson.getAdapter(value.getClass());
if (typeAdapter instanceof ObjectTypeAdapter) {
writer.beginObject();
writer.endObject();
return;
}
typeAdapter.write(writer, value);
}
}

View File

@ -135,10 +135,10 @@ public final class ReflectiveTypeAdapter<T> extends TypeAdapter<T> {
public static class FactoryImpl implements Factory {
public boolean serializeField(Class<?> declaringClazz, Field f, Type declaredType) {
return true;
return !f.isSynthetic();
}
public boolean deserializeField(Class<?> declaringClazz, Field f, Type declaredType) {
return true;
return !f.isSynthetic();
}
public String getFieldName(Class<?> declaringClazz, Field f, Type declaredType) {

View File

@ -0,0 +1,46 @@
/*
* Copyright (C) 2011 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 com.google.gson.internal.bind.MiniGson;
import com.google.gson.internal.bind.TypeAdapter;
import java.util.Arrays;
import java.util.Map;
import junit.framework.TestCase;
public final class ObjectTypeAdapterTest extends TestCase {
private final MiniGson gson = new MiniGson.Builder().build();
private final TypeAdapter<Object> adapter = gson.getAdapter(Object.class);
public void testDeserialize() throws Exception {
Map<?, ?> map = (Map) adapter.fromJson("{a: 5, b: [1, 2, null]}");
assertEquals(5.0, map.get("a"));
assertEquals(Arrays.asList(1.0, 2.0, null), map.get("b"));
}
public void testSerialize() throws Exception {
Object object = new Object() {
Object a = 5;
Object b = Arrays.asList(1, 2, null);
};
assertEquals("{'a':5,'b':[1,2,null]}", adapter.toJson(object).replace("\"", "'"));
}
public void testSerializeObject() throws Exception {
assertEquals("{}", adapter.toJson(new Object()));
}
}