Refactored exclusion strategies so that they can easily be exposed as part of the public API.

This commit is contained in:
Joel Leitch 2009-10-07 09:23:14 +00:00
parent c892738fbb
commit 839b0c2f94
23 changed files with 481 additions and 182 deletions

View File

@ -16,7 +16,6 @@
package com.google.gson; package com.google.gson;
import java.lang.reflect.Field;
/** /**
* Strategy for excluding anonymous and local classes. * Strategy for excluding anonymous and local classes.
@ -25,8 +24,8 @@ import java.lang.reflect.Field;
*/ */
final class AnonymousAndLocalClassExclusionStrategy implements ExclusionStrategy { final class AnonymousAndLocalClassExclusionStrategy implements ExclusionStrategy {
public boolean shouldSkipField(Field f) { public boolean shouldSkipField(FieldAttributes f) {
return isAnonymousOrLocal(f.getType()); return isAnonymousOrLocal(f.getDeclaredClass());
} }
public boolean shouldSkipClass(Class<?> clazz) { public boolean shouldSkipClass(Class<?> clazz) {

View File

@ -16,7 +16,6 @@
package com.google.gson; package com.google.gson;
import java.lang.reflect.Field;
import java.util.Collection; import java.util.Collection;
/** /**
@ -33,7 +32,7 @@ final class DisjunctionExclusionStrategy implements ExclusionStrategy {
this.strategies = strategies; this.strategies = strategies;
} }
public boolean shouldSkipField(Field f) { public boolean shouldSkipField(FieldAttributes f) {
for (ExclusionStrategy strategy : strategies) { for (ExclusionStrategy strategy : strategies) {
if (strategy.shouldSkipField(f)) { if (strategy.shouldSkipField(f)) {
return true; return true;

View File

@ -16,31 +16,55 @@
package com.google.gson; package com.google.gson;
import java.lang.reflect.Field;
/** /**
* A strategy definition that is used by the {@link ObjectNavigator} to * A strategy pattern (see "Design Patterns" written by GoF for some literature on this pattern)
* determine whether or not the field of the object should be ignored during * definition that is used to decide whether or not a field or top-level class should be serialized
* navigation. * (or deserialized) as part of the JSON output/input.
* *
* As well, for now this class is also responsible for excluding entire * <p>The following example show an implementation of an {@code ExclusionStrategy} where a specific
* classes. This is somewhat a mixing of concerns for this object, but * type will be excluded from the output.
* it will suffice for now. We can always break it down into two
* different strategies later.
* *
* <p><pre class="code">
* private static class UserDefinedExclusionStrategy implements ExclusionStrategy {
* private final Class&lt;?&gt; excludedThisClass;
*
* UserDefinedExclusionStrategy(Class&lt;?&gt; excludedThisClass) {
* this.excludedThisClass = excludedThisClass;
* }
*
* public boolean shouldSkipClass(Class&lt;?&gt; clazz) {
* return excludedThisClass.equals(clazz);
* }
*
* public boolean shouldSkipField(FieldAttributes f) {
* return excludedThisClass.equals(f.getDeclaredClass());
* }
* }
*
* ExclusionStrategy excludeStrings = new UserDefinedExclusionStrategy(String.class);
* Gson gson = new GsonBuilder()
* .setExclusionStrategies(excludeStrings)
* .create();
* </pre>
*
* @author Inderjeet Singh
* @author Joel Leitch * @author Joel Leitch
*
* @see GsonBuilder#setExclusionStrategies(ExclusionStrategy...)
*
* @since 1.4
*/ */
interface ExclusionStrategy { interface ExclusionStrategy {
/** /**
* @param f the field object that is under test * @param f the field object that is under test
* @return true if the field should be ignored otherwise false * @return true if the field should be ignored; otherwise false
*/ */
public boolean shouldSkipField(Field f); public boolean shouldSkipField(FieldAttributes f);
/** /**
* @param clazz the class object that is under test * @param clazz the class object that is under test
* @return true if the class should be ignored otherwise false * @return true if the class should be ignored; otherwise false
*/ */
public boolean shouldSkipClass(Class<?> clazz); public boolean shouldSkipClass(Class<?> clazz);
} }

View File

@ -15,13 +15,11 @@
*/ */
package com.google.gson; package com.google.gson;
import java.lang.reflect.Field;
import com.google.gson.annotations.Expose; import com.google.gson.annotations.Expose;
/** /**
* Excludes fields that do not have the {@link Expose} annotation * Excludes fields that do not have the {@link Expose} annotation
* *
* @author Inderjeet Singh * @author Inderjeet Singh
* @author Joel Leitch * @author Joel Leitch
*/ */
@ -31,7 +29,7 @@ final class ExposeAnnotationDeserializationExclusionStrategy implements Exclusio
return false; return false;
} }
public boolean shouldSkipField(Field f) { public boolean shouldSkipField(FieldAttributes f) {
Expose annotation = f.getAnnotation(Expose.class); Expose annotation = f.getAnnotation(Expose.class);
if (annotation == null) { if (annotation == null) {
return true; return true;

View File

@ -18,11 +18,9 @@ package com.google.gson;
import com.google.gson.annotations.Expose; import com.google.gson.annotations.Expose;
import java.lang.reflect.Field;
/** /**
* Excludes fields that do not have the {@link Expose} annotation * Excludes fields that do not have the {@link Expose} annotation
* *
* @author Inderjeet Singh * @author Inderjeet Singh
* @author Joel Leitch * @author Joel Leitch
*/ */
@ -32,7 +30,7 @@ final class ExposeAnnotationSerializationExclusionStrategy implements ExclusionS
return false; return false;
} }
public boolean shouldSkipField(Field f) { public boolean shouldSkipField(FieldAttributes f) {
Expose annotation = f.getAnnotation(Expose.class); Expose annotation = f.getAnnotation(Expose.class);
if (annotation == null) { if (annotation == null) {
return true; return true;

View File

@ -0,0 +1,124 @@
/*
* Copyright (C) 2009 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.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Type;
/**
* A data object that stores attributes of a field.
*
* <p>This class is immutable; therefore, it can be safely shared across threads.
*
* @author Inderjeet Singh
* @author Joel Leitch
*
* @since 1.4
*/
final class FieldAttributes {
private final Field field;
/**
* Constructs a Field Attributes object
* @param f
*/
FieldAttributes(Field f) {
Preconditions.checkNotNull(f);
field = f;
}
/**
* @return the name of the field
*/
public String getName() {
return field.getName();
}
/**
* <p>For example, assume the following class definition:
* <pre class="code">
* public class Foo {
* private String bar;
* private List&lt;String&gt; red;
* }
*
* Type listParmeterizedType = new TypeToken<List<String>>() {}.getType();
* </pre>
*
* <p>This method would return {@code String.class} for the {@code bar} field and
* {@code listParameterizedType} for the {@code red} field.
*
* @return the specific type declared for this field
*/
public Type getDeclaredType() {
return field.getGenericType();
}
/**
* Returns the {@code Class<?>} object that was declared for this field.
*
* <p>For example, assume the following class definition:
* <pre class="code">
* public class Foo {
* private String bar;
* private List&lt;String&gt; red;
* }
* </pre>
*
* <p>This method would return {@code String.class} for the {@code bar} field and
* {@code List.class} for the {@code red} field.
*
* @return the specific class object that was declared for the field
*/
public Class<?> getDeclaredClass() {
return field.getType();
}
/**
* Return the {@link T} annotation object from this field if it exist; otherwise returns
* {@code null}.
*
* @param annotation the class of the annotation that will be retrieved
* @return the annotation instance if it is bound to the field; otherwise {@code null}
*/
public <T extends Annotation> T getAnnotation(Class<T> annotation) {
return field.getAnnotation(annotation);
}
/**
* Returns {@code true} if the field is defined with the {@code modifier}.
*
* <p>This method is meant to be called as:
* <pre class="code">
* boolean hasPublicModifier = fieldAttribute.hasModifier(java.lang.reflect.Modifier.PUBLIC);
* </pre>
*
* @see java.lang.reflect.Modifier
*/
public boolean hasModifier(int modifier) {
return (field.getModifiers() & modifier) != 0;
}
/**
* This is exposed internally only for the
* @return
*/
boolean isSynthetic() {
return field.isSynthetic();
}
}

View File

@ -77,20 +77,27 @@ public final class Gson {
private static final String NULL_STRING = "null"; private static final String NULL_STRING = "null";
static final boolean DEFAULT_JSON_NON_EXECUTABLE = false; static final boolean DEFAULT_JSON_NON_EXECUTABLE = false;
// Default instances of plug-ins // Default instances of plug-ins
static final AnonymousAndLocalClassExclusionStrategy DEFAULT_ANON_LOCAL_CLASS_EXCLUSION_STRATEGY =
new AnonymousAndLocalClassExclusionStrategy();
static final SyntheticFieldExclusionStrategy DEFAULT_SYNTHETIC_FIELD_EXCLUSION_STRATEGY =
new SyntheticFieldExclusionStrategy(true);
static final ModifierBasedExclusionStrategy DEFAULT_MODIFIER_BASED_EXCLUSION_STRATEGY = static final ModifierBasedExclusionStrategy DEFAULT_MODIFIER_BASED_EXCLUSION_STRATEGY =
new ModifierBasedExclusionStrategy(true, new int[] { Modifier.TRANSIENT, Modifier.STATIC }); new ModifierBasedExclusionStrategy(new int[] { Modifier.TRANSIENT, Modifier.STATIC });
static final JsonFormatter DEFAULT_JSON_FORMATTER = new JsonCompactFormatter(); static final JsonFormatter DEFAULT_JSON_FORMATTER = new JsonCompactFormatter();
static final FieldNamingStrategy DEFAULT_NAMING_POLICY = static final FieldNamingStrategy DEFAULT_NAMING_POLICY =
new SerializedNameAnnotationInterceptingNamingPolicy(new JavaFieldNamingPolicy()); new SerializedNameAnnotationInterceptingNamingPolicy(new JavaFieldNamingPolicy());
private static final ExclusionStrategy DEFAULT_EXCLUSION_STRATEGY =
createExclusionStrategy(VersionConstants.IGNORE_VERSIONS);
private static final String JSON_NON_EXECUTABLE_PREFIX = ")]}'\n"; private static final String JSON_NON_EXECUTABLE_PREFIX = ")]}'\n";
private final ExclusionStrategy serializationStrategy; private final ExclusionStrategy serializationStrategy;
private final ExclusionStrategy deserializationStrategy; private final ExclusionStrategy deserializationStrategy;
private final FieldNamingStrategy fieldNamingPolicy; private final FieldNamingStrategy fieldNamingPolicy;
private final MappedObjectConstructor objectConstructor; private final MappedObjectConstructor objectConstructor;
@ -121,7 +128,7 @@ public final class Gson {
* {@link java.math.BigDecimal}, and {@link java.math.BigInteger} classes. If you would prefer * {@link java.math.BigDecimal}, and {@link java.math.BigInteger} classes. If you would prefer
* to change the default representation, you can do so by registering a type adapter through * to change the default representation, you can do so by registering a type adapter through
* {@link GsonBuilder#registerTypeAdapter(Type, Object)}. </li> * {@link GsonBuilder#registerTypeAdapter(Type, Object)}. </li>
* <li>The default Date format is same as {@link java.text.DateFormat#DEFAULT}. This format * <li>The default Date format is same as {@link java.text.DateFormat#DEFAULT}. This format
* ignores the millisecond portion of the date during serialization. You can change * ignores the millisecond portion of the date during serialization. You can change
* this by invoking {@link GsonBuilder#setDateFormat(int)} or * this by invoking {@link GsonBuilder#setDateFormat(int)} or
* {@link GsonBuilder#setDateFormat(String)}. </li> * {@link GsonBuilder#setDateFormat(String)}. </li>
@ -140,25 +147,17 @@ public final class Gson {
* </ul> * </ul>
*/ */
public Gson() { public Gson() {
this(createExclusionStrategy(VersionConstants.IGNORE_VERSIONS), DEFAULT_NAMING_POLICY); this(DEFAULT_EXCLUSION_STRATEGY, DEFAULT_EXCLUSION_STRATEGY, DEFAULT_NAMING_POLICY,
new MappedObjectConstructor(DefaultTypeAdapters.getDefaultInstanceCreators()),
DEFAULT_JSON_FORMATTER, false, DefaultTypeAdapters.getDefaultSerializers(),
DefaultTypeAdapters.getDefaultDeserializers(), DEFAULT_JSON_NON_EXECUTABLE);
} }
/** Gson(ExclusionStrategy serializationStrategy, ExclusionStrategy deserializationStrategy,
* Constructs a Gson object with the specified version and the mode of operation while FieldNamingStrategy fieldNamingPolicy, MappedObjectConstructor objectConstructor,
* encountering inner class references. JsonFormatter formatter, boolean serializeNulls,
*/
Gson(ExclusionStrategy strategy, FieldNamingStrategy fieldNamingPolicy) {
this(strategy, strategy, fieldNamingPolicy,
new MappedObjectConstructor(DefaultTypeAdapters.getDefaultInstanceCreators()),
DEFAULT_JSON_FORMATTER, false, DefaultTypeAdapters.getDefaultSerializers(),
DefaultTypeAdapters.getDefaultDeserializers(), DEFAULT_JSON_NON_EXECUTABLE);
}
Gson(ExclusionStrategy serializationStrategy, ExclusionStrategy deserializationStrategy,
FieldNamingStrategy fieldNamingPolicy, MappedObjectConstructor objectConstructor,
JsonFormatter formatter, boolean serializeNulls,
ParameterizedTypeHandlerMap<JsonSerializer<?>> serializers, ParameterizedTypeHandlerMap<JsonSerializer<?>> serializers,
ParameterizedTypeHandlerMap<JsonDeserializer<?>> deserializers, ParameterizedTypeHandlerMap<JsonDeserializer<?>> deserializers,
boolean generateNonExecutableGson) { boolean generateNonExecutableGson) {
this.serializationStrategy = serializationStrategy; this.serializationStrategy = serializationStrategy;
this.deserializationStrategy = deserializationStrategy; this.deserializationStrategy = deserializationStrategy;
@ -170,14 +169,15 @@ public final class Gson {
this.deserializers = deserializers; this.deserializers = deserializers;
this.generateNonExecutableJson = generateNonExecutableGson; this.generateNonExecutableJson = generateNonExecutableGson;
} }
private ObjectNavigatorFactory createDefaultObjectNavigatorFactory(ExclusionStrategy strategy) { private ObjectNavigatorFactory createDefaultObjectNavigatorFactory(ExclusionStrategy strategy) {
return new ObjectNavigatorFactory(strategy, fieldNamingPolicy); return new ObjectNavigatorFactory(strategy, fieldNamingPolicy);
} }
private static ExclusionStrategy createExclusionStrategy(double version) { private static ExclusionStrategy createExclusionStrategy(double version) {
List<ExclusionStrategy> strategies = new LinkedList<ExclusionStrategy>(); List<ExclusionStrategy> strategies = new LinkedList<ExclusionStrategy>();
strategies.add(new AnonymousAndLocalClassExclusionStrategy()); strategies.add(DEFAULT_ANON_LOCAL_CLASS_EXCLUSION_STRATEGY);
strategies.add(DEFAULT_SYNTHETIC_FIELD_EXCLUSION_STRATEGY);
strategies.add(DEFAULT_MODIFIER_BASED_EXCLUSION_STRATEGY); strategies.add(DEFAULT_MODIFIER_BASED_EXCLUSION_STRATEGY);
if (version != VersionConstants.IGNORE_VERSIONS) { if (version != VersionConstants.IGNORE_VERSIONS) {
strategies.add(new VersionExclusionStrategy(version)); strategies.add(new VersionExclusionStrategy(version));
@ -186,7 +186,7 @@ public final class Gson {
} }
/** /**
* This method serializes the specified object into its equivalent representation as a tree of * This method serializes the specified object into its equivalent representation as a tree of
* {@link JsonElement}s. This method should be used when the specified object is not a generic * {@link JsonElement}s. This method should be used when the specified object is not a generic
* type. This method uses {@link Class#getClass()} to get the type for the specified object, but * type. This method uses {@link Class#getClass()} to get the type for the specified object, but
* the {@code getClass()} loses the generic type information because of the Type Erasure feature * the {@code getClass()} loses the generic type information because of the Type Erasure feature
@ -207,9 +207,9 @@ public final class Gson {
/** /**
* This method serializes the specified object, including those of generic types, into its * This method serializes the specified object, including those of generic types, into its
* equivalent representation as a tree of {@link JsonElement}s. This method must be used if the * equivalent representation as a tree of {@link JsonElement}s. This method must be used if the
* specified object is a generic type. For non-generic objects, use {@link #toJsonTree(Object)} * specified object is a generic type. For non-generic objects, use {@link #toJsonTree(Object)}
* instead. * instead.
* *
* @param src the object for which JSON representation is to be created * @param src the object for which JSON representation is to be created
* @param typeOfSrc The specific genericized type of src. You can obtain * @param typeOfSrc The specific genericized type of src. You can obtain
@ -318,7 +318,7 @@ public final class Gson {
/** /**
* Converts a tree of {@link JsonElement}s into its equivalent JSON representation. * Converts a tree of {@link JsonElement}s into its equivalent JSON representation.
* *
* @param jsonElement root of a tree of {@link JsonElement}s * @param jsonElement root of a tree of {@link JsonElement}s
* @return JSON String representation of the tree * @return JSON String representation of the tree
* @since 1.4 * @since 1.4
@ -328,10 +328,10 @@ public final class Gson {
toJson(jsonElement, writer); toJson(jsonElement, writer);
return writer.toString(); return writer.toString();
} }
/** /**
* Writes out the equivalent JSON for a tree of {@link JsonElement}s. * Writes out the equivalent JSON for a tree of {@link JsonElement}s.
* *
* @param jsonElement root of a tree of {@link JsonElement}s * @param jsonElement root of a tree of {@link JsonElement}s
* @param writer Writer to which the Json representation needs to be written * @param writer Writer to which the Json representation needs to be written
* @since 1.4 * @since 1.4
@ -347,7 +347,7 @@ public final class Gson {
formatter.format(jsonElement, writer, serializeNulls); formatter.format(jsonElement, writer, serializeNulls);
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
/** /**
@ -452,9 +452,9 @@ public final class Gson {
* Therefore, this method should not be used if the desired type is a generic type. Note that * Therefore, this method should not be used if the desired type is a generic type. Note that
* this method works fine if the any of the fields of the specified object are generics, just the * this method works fine if the any of the fields of the specified object are generics, just the
* object itself should not be a generic type. For the cases when the object is of generic type, * object itself should not be a generic type. For the cases when the object is of generic type,
* invoke {@link #fromJson(JsonElement, Type)}. * invoke {@link #fromJson(JsonElement, Type)}.
* @param <T> the type of the desired object * @param <T> the type of the desired object
* @param json the root of the parse tree of {@link JsonElement}s from which the object is to * @param json the root of the parse tree of {@link JsonElement}s from which the object is to
* be deserialized * be deserialized
* @param classOfT The class of T * @param classOfT The class of T
* @return an object of type T from the json * @return an object of type T from the json
@ -469,10 +469,10 @@ public final class Gson {
/** /**
* This method deserializes the Json read from the specified parse tree into an object of the * This method deserializes the Json read from the specified parse tree into an object of the
* specified type. This method is useful if the specified object is a generic type. For * specified type. This method is useful if the specified object is a generic type. For
* non-generic objects, use {@link #fromJson(JsonElement, Class)} instead. * non-generic objects, use {@link #fromJson(JsonElement, Class)} instead.
* *
* @param <T> the type of the desired object * @param <T> the type of the desired object
* @param json the root of the parse tree of {@link JsonElement}s from which the object is to * @param json the root of the parse tree of {@link JsonElement}s from which the object is to
* be deserialized * be deserialized
* @param typeOfT The specific genericized type of src. You can obtain this type by using the * @param typeOfT The specific genericized type of src. You can obtain this type by using the
* {@link com.google.gson.reflect.TypeToken} class. For example, to get the type for * {@link com.google.gson.reflect.TypeToken} class. For example, to get the type for
@ -490,7 +490,7 @@ public final class Gson {
return null; return null;
} }
JsonDeserializationContext context = new JsonDeserializationContextDefault( JsonDeserializationContext context = new JsonDeserializationContextDefault(
createDefaultObjectNavigatorFactory(deserializationStrategy), deserializers, createDefaultObjectNavigatorFactory(deserializationStrategy), deserializers,
objectConstructor); objectConstructor);
T target = (T) context.deserialize(json, typeOfT); T target = (T) context.deserialize(json, typeOfT);
return target; return target;
@ -504,15 +504,15 @@ public final class Gson {
private void writeOutNullString(Appendable writer) throws IOException { private void writeOutNullString(Appendable writer) throws IOException {
writer.append(NULL_STRING); writer.append(NULL_STRING);
} }
@Override @Override
public String toString() { public String toString() {
StringBuilder sb = new StringBuilder("{") StringBuilder sb = new StringBuilder("{")
.append("serializeNulls:").append(serializeNulls) .append("serializeNulls:").append(serializeNulls)
.append(",serializers:").append(serializers) .append(",serializers:").append(serializers)
.append(",deserializers:").append(deserializers) .append(",deserializers:").append(deserializers)
// using the name instanceCreator instead of ObjectConstructor since the users of Gson are // using the name instanceCreator instead of ObjectConstructor since the users of Gson are
// more familiar with the concept of Instance Creators. Moreover, the objectConstructor is // more familiar with the concept of Instance Creators. Moreover, the objectConstructor is
// just a utility class around instance creators, and its toString() only displays them. // just a utility class around instance creators, and its toString() only displays them.
.append(",instanceCreators:").append(objectConstructor) .append(",instanceCreators:").append(objectConstructor)

View File

@ -18,7 +18,9 @@ package com.google.gson;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.text.DateFormat; import java.text.DateFormat;
import java.util.Collection;
import java.util.Date; import java.util.Date;
import java.util.HashSet;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@ -50,17 +52,18 @@ import com.google.gson.DefaultTypeAdapters.DefaultDateTypeAdapter;
* @author Joel Leitch * @author Joel Leitch
*/ */
public final class GsonBuilder { public final class GsonBuilder {
private static final AnonymousAndLocalClassExclusionStrategy anonAndLocalClassExclusionStrategy =
new AnonymousAndLocalClassExclusionStrategy();
private static final InnerClassExclusionStrategy innerClassExclusionStrategy = private static final InnerClassExclusionStrategy innerClassExclusionStrategy =
new InnerClassExclusionStrategy(); new InnerClassExclusionStrategy();
private static final ExposeAnnotationSerializationExclusionStrategy private static final ExposeAnnotationSerializationExclusionStrategy
exposeAnnotationSerializationExclusionStrategy = exposeAnnotationSerializationExclusionStrategy =
new ExposeAnnotationSerializationExclusionStrategy(); new ExposeAnnotationSerializationExclusionStrategy();
private static final ExposeAnnotationDeserializationExclusionStrategy private static final ExposeAnnotationDeserializationExclusionStrategy
exposeAnnotationDeserializationExclusionStrategy = exposeAnnotationDeserializationExclusionStrategy =
new ExposeAnnotationDeserializationExclusionStrategy(); new ExposeAnnotationDeserializationExclusionStrategy();
private final Collection<ExclusionStrategy> exclusionStrategies =
new HashSet<ExclusionStrategy>();
private double ignoreVersionsAfter; private double ignoreVersionsAfter;
private ModifierBasedExclusionStrategy modifierBasedExclusionStrategy; private ModifierBasedExclusionStrategy modifierBasedExclusionStrategy;
private boolean serializeInnerClasses; private boolean serializeInnerClasses;
@ -86,6 +89,10 @@ public final class GsonBuilder {
* {@link #create()}. * {@link #create()}.
*/ */
public GsonBuilder() { public GsonBuilder() {
// add default exclusion strategies
exclusionStrategies.add(Gson.DEFAULT_ANON_LOCAL_CLASS_EXCLUSION_STRATEGY);
exclusionStrategies.add(Gson.DEFAULT_SYNTHETIC_FIELD_EXCLUSION_STRATEGY);
// setup default values // setup default values
ignoreVersionsAfter = VersionConstants.IGNORE_VERSIONS; ignoreVersionsAfter = VersionConstants.IGNORE_VERSIONS;
serializeInnerClasses = true; serializeInnerClasses = true;
@ -129,17 +136,16 @@ public final class GsonBuilder {
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
*/ */
public GsonBuilder excludeFieldsWithModifiers(int... modifiers) { public GsonBuilder excludeFieldsWithModifiers(int... modifiers) {
boolean skipSynthetics = true; modifierBasedExclusionStrategy = new ModifierBasedExclusionStrategy(modifiers);
modifierBasedExclusionStrategy = new ModifierBasedExclusionStrategy(skipSynthetics, modifiers);
return this; return this;
} }
/** /**
* Makes the output JSON non-executable in Javascript by prefixing the generated JSON with some * Makes the output JSON non-executable in Javascript by prefixing the generated JSON with some
* special text. This prevents attacks from third-party sites through script sourcing. See * special text. This prevents attacks from third-party sites through script sourcing. See
* <a href="http://code.google.com/p/google-gson/issues/detail?id=42">Gson Issue 42</a> * <a href="http://code.google.com/p/google-gson/issues/detail?id=42">Gson Issue 42</a>
* for details. * for details.
* *
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
* @since 1.3 * @since 1.3
*/ */
@ -147,7 +153,7 @@ public final class GsonBuilder {
this.generateNonExecutableJson = true; this.generateNonExecutableJson = true;
return this; return this;
} }
/** /**
* Configures Gson to exclude all fields from consideration for serialization or deserialization * Configures Gson to exclude all fields from consideration for serialization or deserialization
* that do not have the {@link com.google.gson.annotations.Expose} annotation. * that do not have the {@link com.google.gson.annotations.Expose} annotation.
@ -216,8 +222,25 @@ public final class GsonBuilder {
* @since 1.3 * @since 1.3
*/ */
public GsonBuilder setFieldNamingStrategy(FieldNamingStrategy fieldNamingStrategy) { public GsonBuilder setFieldNamingStrategy(FieldNamingStrategy fieldNamingStrategy) {
this.fieldNamingPolicy = this.fieldNamingPolicy =
new SerializedNameAnnotationInterceptingNamingPolicy(fieldNamingStrategy); new SerializedNameAnnotationInterceptingNamingPolicy(fieldNamingStrategy);
return this;
}
/**
* Configures Gson to apply a set of exclusion strategies during both serialization and
* deserialization. Each of the {@code strategies} will be applied as a disjunctive rule.
* This means that if one of the {@code strategies} suggests that a field (or class) should be
* skipped then that field (or object) is skipped during serializaiton/deserialization.
*
* @param strategies the set of strategy object to apply during object (de)serialization.
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
* @since 1.4
*/
GsonBuilder setExclusionStrategies(ExclusionStrategy... strategies) {
for (ExclusionStrategy strategy : strategies) {
exclusionStrategies.add(strategy);
}
return this; return this;
} }
@ -412,12 +435,13 @@ public final class GsonBuilder {
* @return an instance of Gson configured with the options currently set in this builder * @return an instance of Gson configured with the options currently set in this builder
*/ */
public Gson create() { public Gson create() {
List<ExclusionStrategy> serializationStrategies = new LinkedList<ExclusionStrategy>(); List<ExclusionStrategy> serializationStrategies =
List<ExclusionStrategy> deserializationStrategies = new LinkedList<ExclusionStrategy>(); new LinkedList<ExclusionStrategy>(exclusionStrategies);
List<ExclusionStrategy> deserializationStrategies =
new LinkedList<ExclusionStrategy>(exclusionStrategies);
serializationStrategies.add(modifierBasedExclusionStrategy); serializationStrategies.add(modifierBasedExclusionStrategy);
deserializationStrategies.add(modifierBasedExclusionStrategy); deserializationStrategies.add(modifierBasedExclusionStrategy);
serializationStrategies.add(anonAndLocalClassExclusionStrategy);
deserializationStrategies.add(anonAndLocalClassExclusionStrategy);
if (!serializeInnerClasses) { if (!serializeInnerClasses) {
serializationStrategies.add(innerClassExclusionStrategy); serializationStrategies.add(innerClassExclusionStrategy);
@ -431,9 +455,9 @@ public final class GsonBuilder {
serializationStrategies.add(exposeAnnotationSerializationExclusionStrategy); serializationStrategies.add(exposeAnnotationSerializationExclusionStrategy);
deserializationStrategies.add(exposeAnnotationDeserializationExclusionStrategy); deserializationStrategies.add(exposeAnnotationDeserializationExclusionStrategy);
} }
ExclusionStrategy serializationExclusionStrategy = ExclusionStrategy serializationExclusionStrategy =
new DisjunctionExclusionStrategy(serializationStrategies); new DisjunctionExclusionStrategy(serializationStrategies);
ExclusionStrategy deserializationExclusionStrategy = ExclusionStrategy deserializationExclusionStrategy =
new DisjunctionExclusionStrategy(deserializationStrategies); new DisjunctionExclusionStrategy(deserializationStrategies);
ParameterizedTypeHandlerMap<JsonSerializer<?>> customSerializers = serializers.copyOf(); ParameterizedTypeHandlerMap<JsonSerializer<?>> customSerializers = serializers.copyOf();
@ -449,17 +473,17 @@ public final class GsonBuilder {
ParameterizedTypeHandlerMap<InstanceCreator<?>> customInstanceCreators = ParameterizedTypeHandlerMap<InstanceCreator<?>> customInstanceCreators =
instanceCreators.copyOf(); instanceCreators.copyOf();
customInstanceCreators.registerIfAbsent(DefaultTypeAdapters.getDefaultInstanceCreators()); customInstanceCreators.registerIfAbsent(DefaultTypeAdapters.getDefaultInstanceCreators());
customSerializers.makeUnmodifiable(); customSerializers.makeUnmodifiable();
customDeserializers.makeUnmodifiable(); customDeserializers.makeUnmodifiable();
instanceCreators.makeUnmodifiable(); instanceCreators.makeUnmodifiable();
MappedObjectConstructor objConstructor = new MappedObjectConstructor(customInstanceCreators); MappedObjectConstructor objConstructor = new MappedObjectConstructor(customInstanceCreators);
JsonFormatter formatter = prettyPrinting ? JsonFormatter formatter = prettyPrinting ?
new JsonPrintFormatter(escapeHtmlChars) : new JsonCompactFormatter(escapeHtmlChars); new JsonPrintFormatter(escapeHtmlChars) : new JsonCompactFormatter(escapeHtmlChars);
Gson gson = new Gson(serializationExclusionStrategy, deserializationExclusionStrategy, Gson gson = new Gson(serializationExclusionStrategy, deserializationExclusionStrategy,
fieldNamingPolicy, objConstructor, formatter, serializeNulls, customSerializers, fieldNamingPolicy, objConstructor, formatter, serializeNulls, customSerializers,
customDeserializers, generateNonExecutableJson); customDeserializers, generateNonExecutableJson);
return gson; return gson;
} }
@ -476,7 +500,7 @@ public final class GsonBuilder {
} else if (dateStyle != DateFormat.DEFAULT && timeStyle != DateFormat.DEFAULT) { } else if (dateStyle != DateFormat.DEFAULT && timeStyle != DateFormat.DEFAULT) {
dateTypeAdapter = new DefaultDateTypeAdapter(dateStyle, timeStyle); dateTypeAdapter = new DefaultDateTypeAdapter(dateStyle, timeStyle);
} }
if (dateTypeAdapter != null) { if (dateTypeAdapter != null) {
serializers.register(Date.class, dateTypeAdapter); serializers.register(Date.class, dateTypeAdapter);
deserializers.register(Date.class, dateTypeAdapter); deserializers.register(Date.class, dateTypeAdapter);

View File

@ -16,7 +16,6 @@
package com.google.gson; package com.google.gson;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
/** /**
@ -26,8 +25,8 @@ import java.lang.reflect.Modifier;
*/ */
class InnerClassExclusionStrategy implements ExclusionStrategy { class InnerClassExclusionStrategy implements ExclusionStrategy {
public boolean shouldSkipField(Field f) { public boolean shouldSkipField(FieldAttributes f) {
return isInnerClass(f.getType()); return isInnerClass(f.getDeclaredClass());
} }
public boolean shouldSkipClass(Class<?> clazz) { public boolean shouldSkipClass(Class<?> clazz) {
@ -37,7 +36,7 @@ class InnerClassExclusionStrategy implements ExclusionStrategy {
private boolean isInnerClass(Class<?> clazz) { private boolean isInnerClass(Class<?> clazz) {
return clazz.isMemberClass() && !isStatic(clazz); return clazz.isMemberClass() && !isStatic(clazz);
} }
private boolean isStatic(Class<?> clazz) { private boolean isStatic(Class<?> clazz) {
return (clazz.getModifiers() & Modifier.STATIC) != 0; return (clazz.getModifiers() & Modifier.STATIC) != 0;
} }

View File

@ -16,7 +16,6 @@
package com.google.gson; package com.google.gson;
import java.lang.reflect.Field;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
@ -28,11 +27,9 @@ import java.util.HashSet;
* @author Joel Leitch * @author Joel Leitch
*/ */
final class ModifierBasedExclusionStrategy implements ExclusionStrategy { final class ModifierBasedExclusionStrategy implements ExclusionStrategy {
private final boolean skipSyntheticField;
private final Collection<Integer> modifiers; private final Collection<Integer> modifiers;
public ModifierBasedExclusionStrategy(boolean skipSyntheticFields, int... modifiers) { public ModifierBasedExclusionStrategy(int... modifiers) {
this.skipSyntheticField = skipSyntheticFields;
this.modifiers = new HashSet<Integer>(); this.modifiers = new HashSet<Integer>();
if (modifiers != null) { if (modifiers != null) {
for (int modifier : modifiers) { for (int modifier : modifiers) {
@ -41,13 +38,9 @@ final class ModifierBasedExclusionStrategy implements ExclusionStrategy {
} }
} }
public boolean shouldSkipField(Field f) { public boolean shouldSkipField(FieldAttributes f) {
if (skipSyntheticField && f.isSynthetic()) {
return true;
}
int objectModifiers = f.getModifiers();
for (int modifier : modifiers) { for (int modifier : modifiers) {
if ((objectModifiers & modifier) != 0) { if (f.hasModifier(modifier)) {
return true; return true;
} }
} }

View File

@ -16,7 +16,6 @@
package com.google.gson; package com.google.gson;
import java.lang.reflect.Field;
/** /**
* This acts as a "Null Object" pattern for the {@link ExclusionStrategy}. * This acts as a "Null Object" pattern for the {@link ExclusionStrategy}.
@ -28,7 +27,7 @@ import java.lang.reflect.Field;
*/ */
final class NullExclusionStrategy implements ExclusionStrategy { final class NullExclusionStrategy implements ExclusionStrategy {
public boolean shouldSkipField(Field f) { public boolean shouldSkipField(FieldAttributes f) {
return false; return false;
} }

View File

@ -62,7 +62,7 @@ final class ObjectNavigator {
* This is called to visit a field of the current object using a custom handler * This is called to visit a field of the current object using a custom handler
*/ */
public boolean visitFieldUsingCustomHandler(Field f, Type actualTypeOfField, Object parent); public boolean visitFieldUsingCustomHandler(Field f, Type actualTypeOfField, Object parent);
/** /**
* Retrieve the current target * Retrieve the current target
*/ */
@ -102,7 +102,7 @@ final class ObjectNavigator {
if (exclusionStrategy.shouldSkipClass(objTypeInfo.getRawClass())) { if (exclusionStrategy.shouldSkipClass(objTypeInfo.getRawClass())) {
return; return;
} }
visitor.start(objTypePair); visitor.start(objTypePair);
try { try {
if (objTypeInfo.isArray()) { if (objTypeInfo.isArray()) {
visitor.visitArray(objectToVisit, objTypePair.getType()); visitor.visitArray(objectToVisit, objTypePair.getType());
@ -141,12 +141,12 @@ final class ObjectNavigator {
Field[] fields = clazz.getDeclaredFields(); Field[] fields = clazz.getDeclaredFields();
AccessibleObject.setAccessible(fields, true); AccessibleObject.setAccessible(fields, true);
for (Field f : fields) { for (Field f : fields) {
if (exclusionStrategy.shouldSkipField(f)) { if (exclusionStrategy.shouldSkipField(new FieldAttributes(f))) {
continue; // skip continue; // skip
} else { } else {
TypeInfo fieldTypeInfo = TypeInfoFactory.getTypeInfoForField(f, objTypePair.getType()); TypeInfo fieldTypeInfo = TypeInfoFactory.getTypeInfoForField(f, objTypePair.getType());
Type actualTypeOfField = fieldTypeInfo.getActualType(); Type actualTypeOfField = fieldTypeInfo.getActualType();
boolean visitedWithCustomHandler = boolean visitedWithCustomHandler =
visitor.visitFieldUsingCustomHandler(f, actualTypeOfField, obj); visitor.visitFieldUsingCustomHandler(f, actualTypeOfField, obj);
if (!visitedWithCustomHandler) { if (!visitedWithCustomHandler) {
if (fieldTypeInfo.isArray()) { if (fieldTypeInfo.isArray()) {

View File

@ -0,0 +1,44 @@
/*
* Copyright (C) 2009 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;
/**
* A data object that stores attributes of a field.
*
* <p>This class is immutable; therefore, it can be safely shared across threads.
*
* @author Inderjeet Singh
* @author Joel Leitch
*
* @since 1.4
*/
class SyntheticFieldExclusionStrategy implements ExclusionStrategy {
private final boolean skipSyntheticFields;
SyntheticFieldExclusionStrategy(boolean skipSyntheticFields) {
this.skipSyntheticFields = skipSyntheticFields;
}
public boolean shouldSkipClass(Class<?> clazz) {
return false;
}
public boolean shouldSkipField(FieldAttributes f) {
return skipSyntheticFields && f.isSynthetic();
}
}

View File

@ -19,9 +19,6 @@ package com.google.gson;
import com.google.gson.annotations.Since; import com.google.gson.annotations.Since;
import com.google.gson.annotations.Until; import com.google.gson.annotations.Until;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
/** /**
* This strategy will exclude any files and/or class that are passed the * This strategy will exclude any files and/or class that are passed the
* {@link #version} value. * {@link #version} value.
@ -36,36 +33,31 @@ final class VersionExclusionStrategy implements ExclusionStrategy {
this.version = version; this.version = version;
} }
public boolean shouldSkipField(Field f) { public boolean shouldSkipField(FieldAttributes f) {
return !isValidVersion(f.getAnnotations()); return !isValidVersion(f.getAnnotation(Since.class), f.getAnnotation(Until.class));
} }
public boolean shouldSkipClass(Class<?> clazz) { public boolean shouldSkipClass(Class<?> clazz) {
return !isValidVersion(clazz.getAnnotations()); return !isValidVersion(clazz.getAnnotation(Since.class), clazz.getAnnotation(Until.class));
} }
private boolean isValidVersion(Annotation[] annotations) { private boolean isValidVersion(Since since, Until until) {
for (Annotation annotation : annotations) { return (isValidSince(since) && isValidUntil(until));
if (!isValidSince(annotation) || !isValidUntil(annotation)) {
return false;
}
}
return true;
} }
private boolean isValidSince(Annotation annotation) { private boolean isValidSince(Since annotation) {
if (annotation instanceof Since) { if (annotation != null) {
double annotationVersion = ((Since) annotation).value(); double annotationVersion = annotation.value();
if (annotationVersion > version) { if (annotationVersion > version) {
return false; return false;
} }
} }
return true; return true;
} }
private boolean isValidUntil(Annotation annotation) { private boolean isValidUntil(Until annotation) {
if (annotation instanceof Until) { if (annotation != null) {
double annotationVersion = ((Until) annotation).value(); double annotationVersion = annotation.value();
if (annotationVersion <= version) { if (annotationVersion <= version) {
return false; return false;
} }

View File

@ -16,15 +16,11 @@
package com.google.gson; package com.google.gson;
import com.google.gson.DisjunctionExclusionStrategy;
import com.google.gson.ExclusionStrategy;
import junit.framework.TestCase;
import java.lang.reflect.Field;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import junit.framework.TestCase;
/** /**
* Unit tests for the {@link DisjunctionExclusionStrategy} class. * Unit tests for the {@link DisjunctionExclusionStrategy} class.
* *
@ -35,12 +31,13 @@ public class DisjunctionExclusionStrategyTest extends TestCase {
private static final ExclusionStrategy FALSE_STRATEGY = new MockExclusionStrategy(false, false); private static final ExclusionStrategy FALSE_STRATEGY = new MockExclusionStrategy(false, false);
private static final ExclusionStrategy TRUE_STRATEGY = new MockExclusionStrategy(true, true); private static final ExclusionStrategy TRUE_STRATEGY = new MockExclusionStrategy(true, true);
private static final Class<?> CLAZZ = String.class; private static final Class<?> CLAZZ = String.class;
private static final Field FIELD = CLAZZ.getFields()[0]; private static final FieldAttributes FIELD = new FieldAttributes(CLAZZ.getFields()[0]);
public void testBadInstantiation() throws Exception { public void testBadInstantiation() throws Exception {
try { try {
List<ExclusionStrategy> constructorParam = null; List<ExclusionStrategy> constructorParam = null;
new DisjunctionExclusionStrategy(constructorParam); new DisjunctionExclusionStrategy(constructorParam);
fail("Should throw an exception");
} catch (IllegalArgumentException expected) { } } catch (IllegalArgumentException expected) { }
} }

View File

@ -39,25 +39,25 @@ public class ExposeAnnotationDeserializationExclusionStrategyTest extends TestCa
public void testNeverSkipClasses() throws Exception { public void testNeverSkipClasses() throws Exception {
assertFalse(strategy.shouldSkipClass(MockObject.class)); assertFalse(strategy.shouldSkipClass(MockObject.class));
} }
public void testSkipNonAnnotatedFields() throws Exception { public void testSkipNonAnnotatedFields() throws Exception {
Field f = MockObject.class.getField("hiddenField"); Field f = MockObject.class.getField("hiddenField");
assertTrue(strategy.shouldSkipField(f)); assertTrue(strategy.shouldSkipField(new FieldAttributes(f)));
} }
public void testSkipExplicitlySkippedFields() throws Exception { public void testSkipExplicitlySkippedFields() throws Exception {
Field f = MockObject.class.getField("explicitlyHiddenField"); Field f = MockObject.class.getField("explicitlyHiddenField");
assertTrue(strategy.shouldSkipField(f)); assertTrue(strategy.shouldSkipField(new FieldAttributes(f)));
} }
public void testNeverSkipExposedAnnotatedFields() throws Exception { public void testNeverSkipExposedAnnotatedFields() throws Exception {
Field f = MockObject.class.getField("exposedField"); Field f = MockObject.class.getField("exposedField");
assertFalse(strategy.shouldSkipField(f)); assertFalse(strategy.shouldSkipField(new FieldAttributes(f)));
} }
public void testNeverSkipExplicitlyExposedAnnotatedFields() throws Exception { public void testNeverSkipExplicitlyExposedAnnotatedFields() throws Exception {
Field f = MockObject.class.getField("explicitlyExposedField"); Field f = MockObject.class.getField("explicitlyExposedField");
assertFalse(strategy.shouldSkipField(f)); assertFalse(strategy.shouldSkipField(new FieldAttributes(f)));
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")
@ -67,7 +67,7 @@ public class ExposeAnnotationDeserializationExclusionStrategyTest extends TestCa
@Expose(deserialize=true) @Expose(deserialize=true)
public final int explicitlyExposedField = 0; public final int explicitlyExposedField = 0;
@Expose(deserialize=false) @Expose(deserialize=false)
public final int explicitlyHiddenField = 0; public final int explicitlyHiddenField = 0;

View File

@ -39,25 +39,25 @@ public class ExposeAnnotationSerializationExclusionStrategyTest extends TestCase
public void testNeverSkipClasses() throws Exception { public void testNeverSkipClasses() throws Exception {
assertFalse(strategy.shouldSkipClass(MockObject.class)); assertFalse(strategy.shouldSkipClass(MockObject.class));
} }
public void testSkipNonAnnotatedFields() throws Exception { public void testSkipNonAnnotatedFields() throws Exception {
Field f = MockObject.class.getField("hiddenField"); Field f = MockObject.class.getField("hiddenField");
assertTrue(strategy.shouldSkipField(f)); assertTrue(strategy.shouldSkipField(new FieldAttributes(f)));
} }
public void testSkipExplicitlySkippedFields() throws Exception { public void testSkipExplicitlySkippedFields() throws Exception {
Field f = MockObject.class.getField("explicitlyHiddenField"); Field f = MockObject.class.getField("explicitlyHiddenField");
assertTrue(strategy.shouldSkipField(f)); assertTrue(strategy.shouldSkipField(new FieldAttributes(f)));
} }
public void testNeverSkipExposedAnnotatedFields() throws Exception { public void testNeverSkipExposedAnnotatedFields() throws Exception {
Field f = MockObject.class.getField("exposedField"); Field f = MockObject.class.getField("exposedField");
assertFalse(strategy.shouldSkipField(f)); assertFalse(strategy.shouldSkipField(new FieldAttributes(f)));
} }
public void testNeverSkipExplicitlyExposedAnnotatedFields() throws Exception { public void testNeverSkipExplicitlyExposedAnnotatedFields() throws Exception {
Field f = MockObject.class.getField("explicitlyExposedField"); Field f = MockObject.class.getField("explicitlyExposedField");
assertFalse(strategy.shouldSkipField(f)); assertFalse(strategy.shouldSkipField(new FieldAttributes(f)));
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")
@ -67,7 +67,7 @@ public class ExposeAnnotationSerializationExclusionStrategyTest extends TestCase
@Expose(serialize=true) @Expose(serialize=true)
public final int explicitlyExposedField = 0; public final int explicitlyExposedField = 0;
@Expose(serialize=false) @Expose(serialize=false)
public final int explicitlyHiddenField = 0; public final int explicitlyHiddenField = 0;

View File

@ -0,0 +1,77 @@
/*
* 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.Modifier;
import java.lang.reflect.Type;
import java.util.List;
import junit.framework.TestCase;
import com.google.gson.reflect.TypeToken;
/**
* Unit tests for the {@link FieldAttributes} class.
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
public class FieldAttributesTest extends TestCase {
private FieldAttributes fieldAttributes;
@Override
protected void setUp() throws Exception {
super.setUp();
fieldAttributes = new FieldAttributes(Foo.class.getField("bar"));
}
public void testNullField() throws Exception {
try {
new FieldAttributes(null);
fail("Field parameter can not be null");
} catch (IllegalArgumentException expected) { }
}
public void testModifiers() throws Exception {
assertFalse(fieldAttributes.hasModifier(Modifier.STATIC));
assertFalse(fieldAttributes.hasModifier(Modifier.FINAL));
assertFalse(fieldAttributes.hasModifier(Modifier.ABSTRACT));
assertFalse(fieldAttributes.hasModifier(Modifier.VOLATILE));
assertFalse(fieldAttributes.hasModifier(Modifier.PROTECTED));
assertTrue(fieldAttributes.hasModifier(Modifier.PUBLIC));
assertTrue(fieldAttributes.hasModifier(Modifier.TRANSIENT));
}
public void testIsSynthetic() throws Exception {
assertFalse(fieldAttributes.isSynthetic());
}
public void testName() throws Exception {
assertEquals("bar", fieldAttributes.getName());
}
public void testDeclaredTypeAndClass() throws Exception {
Type expectedType = new TypeToken<List<String>>() {}.getType();
assertEquals(expectedType, fieldAttributes.getDeclaredType());
assertEquals(List.class, fieldAttributes.getDeclaredClass());
}
private static class Foo {
public transient List<String> bar;
}
}

View File

@ -16,11 +16,13 @@
package com.google.gson; package com.google.gson;
import com.google.gson.common.TestTypes.ClassWithNoFields; import java.lang.reflect.Modifier;
import java.util.LinkedList;
import junit.framework.TestCase; import junit.framework.TestCase;
import java.lang.reflect.Modifier; import com.google.gson.common.TestTypes;
import com.google.gson.common.TestTypes.ClassWithNoFields;
/** /**
* Functional tests for Gson that depend on some internal package-protected elements of * Functional tests for Gson that depend on some internal package-protected elements of
@ -32,11 +34,45 @@ import java.lang.reflect.Modifier;
*/ */
public class FunctionWithInternalDependenciesTest extends TestCase { public class FunctionWithInternalDependenciesTest extends TestCase {
public void testAnonymousLocalClassesSerialization() { public void testAnonymousLocalClassesSerialization() throws Exception {
Gson gson = new Gson(new ModifierBasedExclusionStrategy( LinkedList<ExclusionStrategy> strategies = new LinkedList<ExclusionStrategy>();
true, Modifier.TRANSIENT, Modifier.STATIC), Gson.DEFAULT_NAMING_POLICY); strategies.add(new SyntheticFieldExclusionStrategy(true));
strategies.add(new ModifierBasedExclusionStrategy(Modifier.TRANSIENT, Modifier.STATIC));
ExclusionStrategy exclusionStrategy = new DisjunctionExclusionStrategy(strategies);
Gson gson = new Gson(exclusionStrategy, exclusionStrategy, Gson.DEFAULT_NAMING_POLICY,
new MappedObjectConstructor(DefaultTypeAdapters.getDefaultInstanceCreators()),
Gson.DEFAULT_JSON_FORMATTER, false, DefaultTypeAdapters.getDefaultSerializers(),
DefaultTypeAdapters.getDefaultDeserializers(), Gson.DEFAULT_JSON_NON_EXECUTABLE);
assertEquals("{}", gson.toJson(new ClassWithNoFields() { assertEquals("{}", gson.toJson(new ClassWithNoFields() {
// empty anonymous class // empty anonymous class
})); }));
} }
// TODO(Joel): Move this to some other functional test once exclusion policies are
// available to the public
public void testUserDefinedExclusionPolicies() throws Exception {
Gson gson = new GsonBuilder()
.setExclusionStrategies(new UserDefinedExclusionStrategy(String.class))
.create();
String json = gson.toJson(new TestTypes.StringWrapper("someValue"));
assertEquals("{}", json);
}
private static class UserDefinedExclusionStrategy implements ExclusionStrategy {
private final Class<?> excludedThisClass;
UserDefinedExclusionStrategy(Class<?> excludedThisClass) {
this.excludedThisClass = excludedThisClass;
}
public boolean shouldSkipClass(Class<?> clazz) {
return excludedThisClass.equals(clazz);
}
public boolean shouldSkipField(FieldAttributes f) {
return excludedThisClass.equals(f.getDeclaredClass());
}
}
} }

View File

@ -30,7 +30,7 @@ public class InnerClassExclusionStrategyTest extends TestCase {
public StaticNestedClass staticNestedClass; public StaticNestedClass staticNestedClass;
private InnerClassExclusionStrategy strategy; private InnerClassExclusionStrategy strategy;
@Override @Override
protected void setUp() throws Exception { protected void setUp() throws Exception {
super.setUp(); super.setUp();
@ -43,25 +43,25 @@ public class InnerClassExclusionStrategyTest extends TestCase {
Class<?> clazz = innerClass.getClass(); Class<?> clazz = innerClass.getClass();
assertTrue(strategy.shouldSkipClass(clazz)); assertTrue(strategy.shouldSkipClass(clazz));
} }
public void testExcludeInnerClassField() throws Exception { public void testExcludeInnerClassField() throws Exception {
Field f = getClass().getField("innerClass"); Field f = getClass().getField("innerClass");
assertTrue(strategy.shouldSkipField(f)); assertTrue(strategy.shouldSkipField(new FieldAttributes(f)));
} }
public void testIncludeStaticNestedClassObject() throws Exception { public void testIncludeStaticNestedClassObject() throws Exception {
Class<?> clazz = staticNestedClass.getClass(); Class<?> clazz = staticNestedClass.getClass();
assertFalse(strategy.shouldSkipClass(clazz)); assertFalse(strategy.shouldSkipClass(clazz));
} }
public void testIncludeStaticNestedClassField() throws Exception { public void testIncludeStaticNestedClassField() throws Exception {
Field f = getClass().getField("staticNestedClass"); Field f = getClass().getField("staticNestedClass");
assertFalse(strategy.shouldSkipField(f)); assertFalse(strategy.shouldSkipField(new FieldAttributes(f)));
} }
class InnerClass { class InnerClass {
} }
static class StaticNestedClass { static class StaticNestedClass {
} }
} }

View File

@ -16,13 +16,10 @@
package com.google.gson; package com.google.gson;
import com.google.gson.ExclusionStrategy;
import java.lang.reflect.Field;
/** /**
* This is a configurable {@link ExclusionStrategy} that can be used for * This is a configurable {@link ExclusionStrategy} that can be used for
* unit testing. * unit testing.
* *
* @author Joel Leitch * @author Joel Leitch
*/ */
@ -35,7 +32,7 @@ public class MockExclusionStrategy implements ExclusionStrategy {
this.skipField = skipField; this.skipField = skipField;
} }
public boolean shouldSkipField(Field f) { public boolean shouldSkipField(FieldAttributes f) {
return skipField; return skipField;
} }

View File

@ -16,8 +16,6 @@
package com.google.gson; package com.google.gson;
import com.google.gson.NullExclusionStrategy;
import junit.framework.TestCase; import junit.framework.TestCase;
/** /**
@ -39,6 +37,7 @@ public class NullExclusionStrategyTest extends TestCase {
} }
public void testNeverSkipsField() throws Exception { public void testNeverSkipsField() throws Exception {
assertFalse(strategy.shouldSkipField("".getClass().getFields()[0])); assertFalse(strategy.shouldSkipField(
new FieldAttributes("".getClass().getFields()[0])));
} }
} }

View File

@ -16,11 +16,11 @@
package com.google.gson; package com.google.gson;
import com.google.gson.annotations.Since; import java.lang.reflect.Field;
import junit.framework.TestCase; import junit.framework.TestCase;
import java.lang.reflect.Field; import com.google.gson.annotations.Since;
/** /**
* Unit tests for the {@link VersionExclusionStrategy} class. * Unit tests for the {@link VersionExclusionStrategy} class.
@ -41,27 +41,27 @@ public class VersionExclusionStrategyTest extends TestCase {
Class<MockObject> clazz = MockObject.class; Class<MockObject> clazz = MockObject.class;
Field f = clazz.getField("someField"); Field f = clazz.getField("someField");
VersionExclusionStrategy strategy = new VersionExclusionStrategy(VERSION); VersionExclusionStrategy strategy = new VersionExclusionStrategy(VERSION);
assertFalse(strategy.shouldSkipClass(clazz)); assertFalse(strategy.shouldSkipClass(clazz));
assertFalse(strategy.shouldSkipField(f)); assertFalse(strategy.shouldSkipField(new FieldAttributes(f)));
} }
public void testClassAndFieldAreBehindInVersion() throws Exception { public void testClassAndFieldAreBehindInVersion() throws Exception {
Class<MockObject> clazz = MockObject.class; Class<MockObject> clazz = MockObject.class;
Field f = clazz.getField("someField"); Field f = clazz.getField("someField");
VersionExclusionStrategy strategy = new VersionExclusionStrategy(VERSION + 1); VersionExclusionStrategy strategy = new VersionExclusionStrategy(VERSION + 1);
assertFalse(strategy.shouldSkipClass(clazz)); assertFalse(strategy.shouldSkipClass(clazz));
assertFalse(strategy.shouldSkipField(f)); assertFalse(strategy.shouldSkipField(new FieldAttributes(f)));
} }
public void testClassAndFieldAreAheadInVersion() throws Exception { public void testClassAndFieldAreAheadInVersion() throws Exception {
Class<MockObject> clazz = MockObject.class; Class<MockObject> clazz = MockObject.class;
Field f = clazz.getField("someField"); Field f = clazz.getField("someField");
VersionExclusionStrategy strategy = new VersionExclusionStrategy(VERSION - 1); VersionExclusionStrategy strategy = new VersionExclusionStrategy(VERSION - 1);
assertTrue(strategy.shouldSkipClass(clazz)); assertTrue(strategy.shouldSkipClass(clazz));
assertTrue(strategy.shouldSkipField(f)); assertTrue(strategy.shouldSkipField(new FieldAttributes(f)));
} }
@Since(VERSION) @Since(VERSION)