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:
parent
376385ac0e
commit
6d50bcea87
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -34,8 +34,8 @@ import java.lang.annotation.Target;
|
||||
* <p><pre>
|
||||
* public class User {
|
||||
* @Expose private String firstName;
|
||||
* @Expose private String lastName;
|
||||
* @Expose private String emailAddress;
|
||||
* @Expose(serialize = false) private String lastName;
|
||||
* @Expose (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;
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user