Added support for deserialization exclusion strategy.

This commit is contained in:
Inderjeet Singh 2011-08-03 01:19:26 +00:00
parent 3331dcdab0
commit f276d13827
7 changed files with 82 additions and 42 deletions

View File

@ -28,9 +28,13 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import com.google.gson.internal.bind.ArrayTypeAdapter;
import com.google.gson.internal.bind.CollectionTypeAdapter;
import com.google.gson.internal.bind.MiniGson;
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;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
@ -183,17 +187,31 @@ public final class Gson {
serializeNulls
serializers
*/
TypeAdapter.Factory factory = new ReflectiveTypeAdapter.FactoryImpl() {
TypeAdapter.Factory reflectiveTypeAdapterFactory =
new ReflectiveTypeAdapter.FactoryImpl() {
@Override
public boolean skipField(Class<?> declaringClazz, Field f, Type declaredType) {
// TODO: support deserialization policy as well
return Gson.this.serializationExclusionStrategy.shouldSkipField(
public boolean serializeField(Class<?> declaringClazz, Field f, Type declaredType) {
return !Gson.this.serializationExclusionStrategy.shouldSkipField(
new FieldAttributes(declaringClazz, f, declaredType));
}
@Override
public boolean deserializeField(Class<?> declaringClazz, Field f, Type declaredType) {
return !Gson.this.deserializationExclusionStrategy.shouldSkipField(
new FieldAttributes(declaringClazz, f, declaredType));
}
};
this.miniGson = new MiniGson.Builder()
.factory(factory)
.withoutDefaultFactories()
.factory(TypeAdapters.BOOLEAN_FACTORY)
.factory(TypeAdapters.INTEGER_FACTORY)
.factory(TypeAdapters.DOUBLE_FACTORY)
.factory(TypeAdapters.LONG_FACTORY)
.factory(TypeAdapters.STRING_FACTORY)
.factory(CollectionTypeAdapter.FACTORY)
.factory(StringToValueMapTypeAdapter.FACTORY)
.factory(ArrayTypeAdapter.FACTORY)
.factory(reflectiveTypeAdapterFactory)
.build();
}

View File

@ -16,11 +16,6 @@
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.Array;
import java.lang.reflect.GenericArrayType;
@ -28,10 +23,16 @@ import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
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;
/**
* Adapt an array of objects.
*/
final class ArrayTypeAdapter<E> extends TypeAdapter<Object> {
public final class ArrayTypeAdapter<E> extends TypeAdapter<Object> {
public static final Factory FACTORY = new Factory() {
public <T> TypeAdapter<T> create(MiniGson context, TypeToken<T> typeToken) {
Type type = typeToken.getType();

View File

@ -16,11 +16,6 @@
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;
@ -31,10 +26,16 @@ import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
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;
/**
* Adapt a homogeneous collection of objects.
*/
final class CollectionTypeAdapter<E> extends TypeAdapter<Collection<E>> {
public final class CollectionTypeAdapter<E> extends TypeAdapter<Collection<E>> {
public static final Factory FACTORY = new Factory() {
public <T> TypeAdapter<T> create(MiniGson context, TypeToken<T> typeToken) {
Type type = typeToken.getType();

View File

@ -50,16 +50,20 @@ public final class MiniGson {
private MiniGson(Builder builder) {
List<TypeAdapter.Factory> factories = new ArrayList<TypeAdapter.Factory>();
factories.add(TypeAdapters.BOOLEAN_FACTORY);
factories.add(TypeAdapters.INTEGER_FACTORY);
factories.add(TypeAdapters.DOUBLE_FACTORY);
factories.add(TypeAdapters.LONG_FACTORY);
factories.add(TypeAdapters.STRING_FACTORY);
if (builder.addDefaultFactories) {
factories.add(TypeAdapters.BOOLEAN_FACTORY);
factories.add(TypeAdapters.INTEGER_FACTORY);
factories.add(TypeAdapters.DOUBLE_FACTORY);
factories.add(TypeAdapters.LONG_FACTORY);
factories.add(TypeAdapters.STRING_FACTORY);
}
factories.addAll(builder.factories);
factories.add(CollectionTypeAdapter.FACTORY);
factories.add(StringToValueMapTypeAdapter.FACTORY);
factories.add(ArrayTypeAdapter.FACTORY);
factories.add(ReflectiveTypeAdapter.FACTORY);
if (builder.addDefaultFactories) {
factories.add(CollectionTypeAdapter.FACTORY);
factories.add(StringToValueMapTypeAdapter.FACTORY);
factories.add(ArrayTypeAdapter.FACTORY);
factories.add(ReflectiveTypeAdapter.FACTORY);
}
this.factories = Collections.unmodifiableList(factories);
}
@ -156,12 +160,18 @@ public final class MiniGson {
public static final class Builder {
private final List<TypeAdapter.Factory> factories = new ArrayList<TypeAdapter.Factory>();
boolean addDefaultFactories = true;
public Builder factory(TypeAdapter.Factory factory) {
factories.add(factory);
return this;
}
public Builder withoutDefaultFactories() {
this.addDefaultFactories = false;
return this;
}
public <T> Builder typeAdapter(final Class<T> type, final TypeAdapter<T> typeAdapter) {
factories.add(TypeAdapters.newFactory(type, typeAdapter));
return this;

View File

@ -60,7 +60,7 @@ public final class ReflectiveTypeAdapter<T> extends TypeAdapter<T> {
while (reader.hasNext()) {
String name = reader.nextName();
BoundField field = map.get(name);
if (field == null) {
if (field == null || !field.deserialized) {
// TODO: define a better policy
reader.skipValue();
} else {
@ -83,8 +83,10 @@ public final class ReflectiveTypeAdapter<T> extends TypeAdapter<T> {
writer.beginObject();
try {
for (BoundField boundField : boundFields) {
writer.name(boundField.name);
boundField.write(writer, value);
if (boundField.serialized) {
writer.name(boundField.name);
boundField.write(writer, value);
}
}
} catch (IllegalAccessException e) {
throw new AssertionError();
@ -93,9 +95,9 @@ public final class ReflectiveTypeAdapter<T> extends TypeAdapter<T> {
}
static BoundField createBoundField(
final MiniGson context, final Field field, final TypeToken<?> fieldType) {
final MiniGson context, final Field field, final TypeToken<?> fieldType, boolean serialize, boolean deserialize) {
// special casing primitives here saves ~5% on Android...
return new BoundField(field.getName()) {
return new BoundField(field.getName(), serialize, deserialize) {
final TypeAdapter<?> typeAdapter = context.getAdapter(fieldType);
@SuppressWarnings("unchecked") // the type adapter and field type always agree
@Override void write(JsonWriter writer, Object value)
@ -112,8 +114,11 @@ public final class ReflectiveTypeAdapter<T> extends TypeAdapter<T> {
}
public static class FactoryImpl implements Factory {
public boolean skipField(Class<?> declaringClazz, Field f, Type declaringType) {
return false;
public boolean serializeField(Class<?> declaringClazz, Field f, Type declaringType) {
return true;
}
public boolean deserializeField(Class<?> declaringClazz, Field f, Type declaringType) {
return true;
}
public <T> TypeAdapter<T> create(MiniGson context, TypeToken<T> type) {
Class<? super T> raw = type.getRawType();
@ -139,13 +144,14 @@ public final class ReflectiveTypeAdapter<T> extends TypeAdapter<T> {
Type declaredType = type.getType();
while (raw != Object.class) {
for (Field field : raw.getDeclaredFields()) {
if (skipField(raw, field, declaredType)) {
continue;
boolean serialize = serializeField(raw, field, declaredType);
boolean deserialize = deserializeField(raw, field, declaredType);
if (serialize || deserialize) {
field.setAccessible(true); // TODO: don't call setAccessible unless necessary
Type fieldType = $Gson$Types.resolve(type.getType(), raw, field.getGenericType());
BoundField boundField = createBoundField(context, field, TypeToken.get(fieldType), serialize, deserialize);
result.put(boundField.name, boundField);
}
field.setAccessible(true); // TODO: don't call setAccessible unless necessary
Type fieldType = $Gson$Types.resolve(type.getType(), raw, field.getGenericType());
BoundField boundField = createBoundField(context, field, TypeToken.get(fieldType));
result.put(boundField.name, boundField);
}
type = TypeToken.get($Gson$Types.resolve(type.getType(), raw, raw.getGenericSuperclass()));
raw = type.getRawType();
@ -156,9 +162,13 @@ public final class ReflectiveTypeAdapter<T> extends TypeAdapter<T> {
static abstract class BoundField {
final String name;
final boolean serialized;
final boolean deserialized;
protected BoundField(String name) {
protected BoundField(String name, boolean serialized, boolean deserialized) {
this.name = name;
this.serialized = serialized;
this.deserialized = deserialized;
}
abstract void write(JsonWriter writer, Object value) throws IOException, IllegalAccessException;

View File

@ -31,7 +31,7 @@ import java.util.Map;
/**
* Adapt a map whose keys are strings.
*/
final class StringToValueMapTypeAdapter<V> extends TypeAdapter<Map<String, V>> {
public final class StringToValueMapTypeAdapter<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();

View File

@ -24,7 +24,7 @@ import java.io.IOException;
/**
* Type adapters for basic types.
*/
final class TypeAdapters {
public final class TypeAdapters {
private TypeAdapters() {}
public static final TypeAdapter<Boolean> BOOLEAN = new TypeAdapter<Boolean>() {