Added serialize and deserialize parameters to the Expose annotation that control whether a field gets exposed during serialization or deserialization.

This commit is contained in:
Inderjeet Singh 2009-05-19 23:47:53 +00:00
parent 376385ac0e
commit 6d50bcea87
6 changed files with 106 additions and 46 deletions

View File

@ -27,11 +27,32 @@ import java.lang.reflect.Field;
*/
class ExposeAnnotationBasedExclusionStrategy implements ExclusionStrategy {
enum Phase {
SERIALIZATION, DESERIALIZATION
}
private final Phase phase;
public ExposeAnnotationBasedExclusionStrategy(Phase phase) {
this.phase = phase;
}
public boolean shouldSkipClass(Class<?> clazz) {
return false;
}
public boolean shouldSkipField(Field f) {
return f.getAnnotation(Expose.class) == null;
Expose annotation = f.getAnnotation(Expose.class);
if (annotation == null) {
return true;
}
switch (phase) {
case SERIALIZATION:
return !annotation.serialize();
case DESERIALIZATION:
return !annotation.deserialize();
default:
throw new IllegalStateException();
}
}
}

View File

@ -87,7 +87,11 @@ public final class Gson {
private static final String JSON_NON_EXECUTABLE_PREFIX = ")]}'\n";
private final ExclusionStrategy strategy;
private final ExclusionStrategy serializationStrategy;
private final ExclusionStrategy deserializationStrategy;
private final FieldNamingStrategy fieldNamingPolicy;
private final MappedObjectConstructor objectConstructor;
@ -145,18 +149,20 @@ public final class Gson {
* encountering inner class references.
*/
Gson(ExclusionStrategy strategy, FieldNamingStrategy fieldNamingPolicy) {
this(strategy, fieldNamingPolicy,
this(strategy, strategy, fieldNamingPolicy,
new MappedObjectConstructor(DefaultTypeAdapters.getDefaultInstanceCreators()),
DEFAULT_JSON_FORMATTER, false, DefaultTypeAdapters.getDefaultSerializers(),
DefaultTypeAdapters.getDefaultDeserializers(), DEFAULT_JSON_NON_EXECUTABLE);
}
Gson(ExclusionStrategy strategy, FieldNamingStrategy fieldNamingPolicy,
MappedObjectConstructor objectConstructor, JsonFormatter formatter, boolean serializeNulls,
Gson(ExclusionStrategy serializationStrategy, ExclusionStrategy deserializationStrategy,
FieldNamingStrategy fieldNamingPolicy, MappedObjectConstructor objectConstructor,
JsonFormatter formatter, boolean serializeNulls,
ParameterizedTypeHandlerMap<JsonSerializer<?>> serializers,
ParameterizedTypeHandlerMap<JsonDeserializer<?>> deserializers,
boolean generateNonExecutableGson) {
this.strategy = strategy;
this.serializationStrategy = serializationStrategy;
this.deserializationStrategy = deserializationStrategy;
this.fieldNamingPolicy = fieldNamingPolicy;
this.objectConstructor = objectConstructor;
this.formatter = formatter;
@ -166,7 +172,7 @@ public final class Gson {
this.generateNonExecutableJson = generateNonExecutableGson;
}
private ObjectNavigatorFactory createDefaultObjectNavigatorFactory() {
private ObjectNavigatorFactory createDefaultObjectNavigatorFactory(ExclusionStrategy strategy) {
return new ObjectNavigatorFactory(strategy, fieldNamingPolicy);
}
@ -223,7 +229,7 @@ public final class Gson {
return JsonNull.createJsonNull();
}
JsonSerializationContext context = new JsonSerializationContextDefault(
createDefaultObjectNavigatorFactory(), serializeNulls, serializers);
createDefaultObjectNavigatorFactory(serializationStrategy), serializeNulls, serializers);
return context.serialize(src, typeOfSrc);
}
@ -459,7 +465,8 @@ public final class Gson {
return null;
}
JsonDeserializationContext context = new JsonDeserializationContextDefault(
createDefaultObjectNavigatorFactory(), deserializers, objectConstructor);
createDefaultObjectNavigatorFactory(deserializationStrategy), deserializers,
objectConstructor);
T target = (T) context.deserialize(json, typeOfT);
return target;
}

View File

@ -23,6 +23,7 @@ import java.util.LinkedList;
import java.util.List;
import com.google.gson.DefaultTypeAdapters.DefaultDateTypeAdapter;
import com.google.gson.ExposeAnnotationBasedExclusionStrategy.Phase;
/**
* <p>Use this builder to construct a {@link Gson} instance when you need to set configuration
@ -54,8 +55,12 @@ public final class GsonBuilder {
new AnonymousAndLocalClassExclusionStrategy();
private static final InnerClassExclusionStrategy innerClassExclusionStrategy =
new InnerClassExclusionStrategy();
private static final ExposeAnnotationBasedExclusionStrategy exposeAnnotationExclusionStrategy =
new ExposeAnnotationBasedExclusionStrategy();
private static final ExposeAnnotationBasedExclusionStrategy
exposeAnnotationSerializationExclusionStrategy =
new ExposeAnnotationBasedExclusionStrategy(Phase.SERIALIZATION);
private static final ExposeAnnotationBasedExclusionStrategy
exposeAnnotationDeserializationExclusionStrategy =
new ExposeAnnotationBasedExclusionStrategy(Phase.DESERIALIZATION);
private double ignoreVersionsAfter;
private ModifierBasedExclusionStrategy modifierBasedExclusionStrategy;
@ -408,20 +413,29 @@ public final class GsonBuilder {
* @return an instance of Gson configured with the options currently set in this builder
*/
public Gson create() {
List<ExclusionStrategy> strategies = new LinkedList<ExclusionStrategy>();
strategies.add(modifierBasedExclusionStrategy);
strategies.add(anonAndLocalClassExclusionStrategy);
List<ExclusionStrategy> serializationStrategies = new LinkedList<ExclusionStrategy>();
List<ExclusionStrategy> deserializationStrategies = new LinkedList<ExclusionStrategy>();
serializationStrategies.add(modifierBasedExclusionStrategy);
deserializationStrategies.add(modifierBasedExclusionStrategy);
serializationStrategies.add(anonAndLocalClassExclusionStrategy);
deserializationStrategies.add(anonAndLocalClassExclusionStrategy);
if (!serializeInnerClasses) {
strategies.add(innerClassExclusionStrategy);
serializationStrategies.add(innerClassExclusionStrategy);
deserializationStrategies.add(innerClassExclusionStrategy);
}
if (ignoreVersionsAfter != VersionConstants.IGNORE_VERSIONS) {
strategies.add(new VersionExclusionStrategy(ignoreVersionsAfter));
serializationStrategies.add(new VersionExclusionStrategy(ignoreVersionsAfter));
deserializationStrategies.add(new VersionExclusionStrategy(ignoreVersionsAfter));
}
if (excludeFieldsWithoutExposeAnnotation) {
strategies.add(exposeAnnotationExclusionStrategy);
serializationStrategies.add(exposeAnnotationSerializationExclusionStrategy);
deserializationStrategies.add(exposeAnnotationDeserializationExclusionStrategy);
}
ExclusionStrategy exclusionStrategy = new DisjunctionExclusionStrategy(strategies);
ExclusionStrategy serializationExclusionStrategy =
new DisjunctionExclusionStrategy(serializationStrategies);
ExclusionStrategy deserializationExclusionStrategy =
new DisjunctionExclusionStrategy(deserializationStrategies);
ParameterizedTypeHandlerMap<JsonSerializer<?>> customSerializers = serializers.copyOf();
ParameterizedTypeHandlerMap<JsonDeserializer<?>> customDeserializers = deserializers.copyOf();
@ -445,8 +459,9 @@ public final class GsonBuilder {
JsonFormatter formatter = prettyPrinting ?
new JsonPrintFormatter(escapeHtmlChars) : new JsonCompactFormatter(escapeHtmlChars);
Gson gson = new Gson(exclusionStrategy, fieldNamingPolicy, objConstructor,
formatter, serializeNulls, customSerializers, customDeserializers, generateNonExecutableJson);
Gson gson = new Gson(serializationExclusionStrategy, deserializationExclusionStrategy,
fieldNamingPolicy, objConstructor, formatter, serializeNulls, customSerializers,
customDeserializers, generateNonExecutableJson);
return gson;
}

View File

@ -34,8 +34,8 @@ import java.lang.annotation.Target;
* <p><pre>
* public class User {
* &#64Expose private String firstName;
* &#64Expose private String lastName;
* &#64Expose private String emailAddress;
* &#64Expose(serialize = false) private String lastName;
* &#64Expose (serialize = false, deserialize = false) private String emailAddress;
* private String password;
* }
* </pre></p>
@ -45,7 +45,9 @@ import java.lang.annotation.Target;
* with {@code Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create()}
* then the {@code toJson()} and {@code fromJson()} methods of Gson will exclude the
* {@code password} field. This is because the {@code password} field is not marked with the
* {@code @Expose} annotation.
* {@code @Expose} annotation. Gson will also exclude {@code lastName} and {@code emailAddress}
* from serialization since {@code serialize} is set to {@code false}. Similarly, Gson will
* exclude {@code emailAddress} from deserialization since {@code deserialize} is set to false.
*
* <p>Note that another way to achieve the same effect would have been to just mark the
* {@code password} field as {@code transient}, and Gson would have excluded it even with default
@ -58,5 +60,20 @@ import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Expose {
// This is a marker annotation with no additional properties
/**
* If true, the field marked with this annotation is written out in the JSON while serializing.
* If false, the field marked with this annotation is skipped from the serialized output.
* Defaults to true.
* @since 1.4
*/
public boolean serialize() default true;
/**
* If true, the field marked with this annotation is deserialized from the JSON.
* If false, the field marked with this annotation is skipped during deserialization.
* Defaults to true.
* @since 1.4
*/
public boolean deserialize() default true;
}

View File

@ -16,6 +16,7 @@
package com.google.gson;
import com.google.gson.ExposeAnnotationBasedExclusionStrategy.Phase;
import com.google.gson.annotations.Expose;
import junit.framework.TestCase;
@ -33,7 +34,7 @@ public class ExposeAnnotationBasedExclusionStrategyTest extends TestCase {
@Override
protected void setUp() throws Exception {
super.setUp();
strategy = new ExposeAnnotationBasedExclusionStrategy();
strategy = new ExposeAnnotationBasedExclusionStrategy(Phase.SERIALIZATION);
}
public void testNeverSkipClasses() throws Exception {

View File

@ -72,11 +72,12 @@ public class ExposeFieldsTest extends TestCase {
}
public void testExposeAnnotationDeserialization() throws Exception {
String json = '{' + "\"a\":" + 3 + ",\"b\":" + 4 + '}';
String json = "{a:3,b:4,d:20.0}";
ClassWithExposedFields target = gson.fromJson(json, ClassWithExposedFields.class);
assertEquals(3, (int) target.a);
assertNull(target.b);
assertFalse(target.d == 20);
}
public void testNoExposedFieldSerialization() throws Exception {
@ -87,7 +88,7 @@ public class ExposeFieldsTest extends TestCase {
}
public void testNoExposedFieldDeserialization() throws Exception {
String json = '{' + "\"a\":" + 4 + ",\"b\":" + 5 + '}';
String json = "{a:4,b:5}";
ClassWithNoExposedFields obj = gson.fromJson(json, ClassWithNoExposedFields.class);
assertEquals(0, obj.a);
@ -112,39 +113,37 @@ public class ExposeFieldsTest extends TestCase {
private static class ClassWithExposedFields {
@Expose private final Integer a;
private final Integer b;
@Expose(serialize = false) final long c;
@Expose(deserialize = false) final double d;
@Expose(serialize = false, deserialize = false) final char e;
ClassWithExposedFields() {
this(null, null);
}
public ClassWithExposedFields(Integer a, Integer b) {
this(a, b, 1L, 2.0, 'a');
}
public ClassWithExposedFields(Integer a, Integer b, long c, double d, char e) {
this.a = a;
this.b = b;
this.c = c;
this.d = d;
this.e = e;
}
public String getExpectedJson() {
if (a == null) {
return "{}";
StringBuilder sb = new StringBuilder("{");
if (a != null) {
sb.append("\"a\":").append(a).append(",");
}
return '{' + "\"a\":" + a + '}';
sb.append("\"d\":").append(d);
sb.append("}");
return sb.toString();
}
public String getExpectedJsonWithoutAnnotations() {
StringBuilder stringBuilder = new StringBuilder();
boolean requiresComma = false;
stringBuilder.append('{');
if (a != null) {
stringBuilder.append("\"a\":").append(a);
requiresComma = true;
}
if (b != null) {
if (requiresComma) {
stringBuilder.append(',');
}
stringBuilder.append("\"b\":").append(b);
}
stringBuilder.append('}');
return stringBuilder.toString();
return String.format("{\"a\":%d,\"b\":%d,\"c\":%d,\"d\":%f,\"e\":\"%c\"}", a, b, c, d, e);
}
}
@ -178,5 +177,5 @@ public class ExposeFieldsTest extends TestCase {
public ClassWithInterfaceField(SomeInterface interfaceField) {
this.interfaceField = interfaceField;
}
}
}
}