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

View File

@ -16,11 +16,6 @@
package com.google.gson.internal.bind; 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.io.IOException;
import java.lang.reflect.Array; import java.lang.reflect.Array;
import java.lang.reflect.GenericArrayType; import java.lang.reflect.GenericArrayType;
@ -28,10 +23,16 @@ import java.lang.reflect.Type;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; 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. * 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 static final Factory FACTORY = new Factory() {
public <T> TypeAdapter<T> create(MiniGson context, TypeToken<T> typeToken) { public <T> TypeAdapter<T> create(MiniGson context, TypeToken<T> typeToken) {
Type type = typeToken.getType(); Type type = typeToken.getType();

View File

@ -16,11 +16,6 @@
package com.google.gson.internal.bind; 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.io.IOException;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.ParameterizedType; import java.lang.reflect.ParameterizedType;
@ -31,10 +26,16 @@ import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Set; 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. * 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 static final Factory FACTORY = new Factory() {
public <T> TypeAdapter<T> create(MiniGson context, TypeToken<T> typeToken) { public <T> TypeAdapter<T> create(MiniGson context, TypeToken<T> typeToken) {
Type type = typeToken.getType(); Type type = typeToken.getType();

View File

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

View File

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

View File

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