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 {
|
class ExposeAnnotationBasedExclusionStrategy implements ExclusionStrategy {
|
||||||
|
|
||||||
|
enum Phase {
|
||||||
|
SERIALIZATION, DESERIALIZATION
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Phase phase;
|
||||||
|
|
||||||
|
public ExposeAnnotationBasedExclusionStrategy(Phase phase) {
|
||||||
|
this.phase = phase;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean shouldSkipClass(Class<?> clazz) {
|
public boolean shouldSkipClass(Class<?> clazz) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean shouldSkipField(Field f) {
|
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 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 FieldNamingStrategy fieldNamingPolicy;
|
||||||
private final MappedObjectConstructor objectConstructor;
|
private final MappedObjectConstructor objectConstructor;
|
||||||
|
|
||||||
@ -145,18 +149,20 @@ public final class Gson {
|
|||||||
* encountering inner class references.
|
* encountering inner class references.
|
||||||
*/
|
*/
|
||||||
Gson(ExclusionStrategy strategy, FieldNamingStrategy fieldNamingPolicy) {
|
Gson(ExclusionStrategy strategy, FieldNamingStrategy fieldNamingPolicy) {
|
||||||
this(strategy, fieldNamingPolicy,
|
this(strategy, strategy, fieldNamingPolicy,
|
||||||
new MappedObjectConstructor(DefaultTypeAdapters.getDefaultInstanceCreators()),
|
new MappedObjectConstructor(DefaultTypeAdapters.getDefaultInstanceCreators()),
|
||||||
DEFAULT_JSON_FORMATTER, false, DefaultTypeAdapters.getDefaultSerializers(),
|
DEFAULT_JSON_FORMATTER, false, DefaultTypeAdapters.getDefaultSerializers(),
|
||||||
DefaultTypeAdapters.getDefaultDeserializers(), DEFAULT_JSON_NON_EXECUTABLE);
|
DefaultTypeAdapters.getDefaultDeserializers(), DEFAULT_JSON_NON_EXECUTABLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
Gson(ExclusionStrategy strategy, FieldNamingStrategy fieldNamingPolicy,
|
Gson(ExclusionStrategy serializationStrategy, ExclusionStrategy deserializationStrategy,
|
||||||
MappedObjectConstructor objectConstructor, JsonFormatter formatter, boolean serializeNulls,
|
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.strategy = strategy;
|
this.serializationStrategy = serializationStrategy;
|
||||||
|
this.deserializationStrategy = deserializationStrategy;
|
||||||
this.fieldNamingPolicy = fieldNamingPolicy;
|
this.fieldNamingPolicy = fieldNamingPolicy;
|
||||||
this.objectConstructor = objectConstructor;
|
this.objectConstructor = objectConstructor;
|
||||||
this.formatter = formatter;
|
this.formatter = formatter;
|
||||||
@ -166,7 +172,7 @@ public final class Gson {
|
|||||||
this.generateNonExecutableJson = generateNonExecutableGson;
|
this.generateNonExecutableJson = generateNonExecutableGson;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ObjectNavigatorFactory createDefaultObjectNavigatorFactory() {
|
private ObjectNavigatorFactory createDefaultObjectNavigatorFactory(ExclusionStrategy strategy) {
|
||||||
return new ObjectNavigatorFactory(strategy, fieldNamingPolicy);
|
return new ObjectNavigatorFactory(strategy, fieldNamingPolicy);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,7 +229,7 @@ public final class Gson {
|
|||||||
return JsonNull.createJsonNull();
|
return JsonNull.createJsonNull();
|
||||||
}
|
}
|
||||||
JsonSerializationContext context = new JsonSerializationContextDefault(
|
JsonSerializationContext context = new JsonSerializationContextDefault(
|
||||||
createDefaultObjectNavigatorFactory(), serializeNulls, serializers);
|
createDefaultObjectNavigatorFactory(serializationStrategy), serializeNulls, serializers);
|
||||||
return context.serialize(src, typeOfSrc);
|
return context.serialize(src, typeOfSrc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -459,7 +465,8 @@ public final class Gson {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
JsonDeserializationContext context = new JsonDeserializationContextDefault(
|
JsonDeserializationContext context = new JsonDeserializationContextDefault(
|
||||||
createDefaultObjectNavigatorFactory(), deserializers, objectConstructor);
|
createDefaultObjectNavigatorFactory(deserializationStrategy), deserializers,
|
||||||
|
objectConstructor);
|
||||||
T target = (T) context.deserialize(json, typeOfT);
|
T target = (T) context.deserialize(json, typeOfT);
|
||||||
return target;
|
return target;
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ import java.util.LinkedList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import com.google.gson.DefaultTypeAdapters.DefaultDateTypeAdapter;
|
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
|
* <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();
|
new AnonymousAndLocalClassExclusionStrategy();
|
||||||
private static final InnerClassExclusionStrategy innerClassExclusionStrategy =
|
private static final InnerClassExclusionStrategy innerClassExclusionStrategy =
|
||||||
new InnerClassExclusionStrategy();
|
new InnerClassExclusionStrategy();
|
||||||
private static final ExposeAnnotationBasedExclusionStrategy exposeAnnotationExclusionStrategy =
|
private static final ExposeAnnotationBasedExclusionStrategy
|
||||||
new ExposeAnnotationBasedExclusionStrategy();
|
exposeAnnotationSerializationExclusionStrategy =
|
||||||
|
new ExposeAnnotationBasedExclusionStrategy(Phase.SERIALIZATION);
|
||||||
|
private static final ExposeAnnotationBasedExclusionStrategy
|
||||||
|
exposeAnnotationDeserializationExclusionStrategy =
|
||||||
|
new ExposeAnnotationBasedExclusionStrategy(Phase.DESERIALIZATION);
|
||||||
|
|
||||||
private double ignoreVersionsAfter;
|
private double ignoreVersionsAfter;
|
||||||
private ModifierBasedExclusionStrategy modifierBasedExclusionStrategy;
|
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
|
* @return an instance of Gson configured with the options currently set in this builder
|
||||||
*/
|
*/
|
||||||
public Gson create() {
|
public Gson create() {
|
||||||
List<ExclusionStrategy> strategies = new LinkedList<ExclusionStrategy>();
|
List<ExclusionStrategy> serializationStrategies = new LinkedList<ExclusionStrategy>();
|
||||||
strategies.add(modifierBasedExclusionStrategy);
|
List<ExclusionStrategy> deserializationStrategies = new LinkedList<ExclusionStrategy>();
|
||||||
strategies.add(anonAndLocalClassExclusionStrategy);
|
serializationStrategies.add(modifierBasedExclusionStrategy);
|
||||||
|
deserializationStrategies.add(modifierBasedExclusionStrategy);
|
||||||
|
serializationStrategies.add(anonAndLocalClassExclusionStrategy);
|
||||||
|
deserializationStrategies.add(anonAndLocalClassExclusionStrategy);
|
||||||
|
|
||||||
if (!serializeInnerClasses) {
|
if (!serializeInnerClasses) {
|
||||||
strategies.add(innerClassExclusionStrategy);
|
serializationStrategies.add(innerClassExclusionStrategy);
|
||||||
|
deserializationStrategies.add(innerClassExclusionStrategy);
|
||||||
}
|
}
|
||||||
if (ignoreVersionsAfter != VersionConstants.IGNORE_VERSIONS) {
|
if (ignoreVersionsAfter != VersionConstants.IGNORE_VERSIONS) {
|
||||||
strategies.add(new VersionExclusionStrategy(ignoreVersionsAfter));
|
serializationStrategies.add(new VersionExclusionStrategy(ignoreVersionsAfter));
|
||||||
|
deserializationStrategies.add(new VersionExclusionStrategy(ignoreVersionsAfter));
|
||||||
}
|
}
|
||||||
if (excludeFieldsWithoutExposeAnnotation) {
|
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<JsonSerializer<?>> customSerializers = serializers.copyOf();
|
||||||
ParameterizedTypeHandlerMap<JsonDeserializer<?>> customDeserializers = deserializers.copyOf();
|
ParameterizedTypeHandlerMap<JsonDeserializer<?>> customDeserializers = deserializers.copyOf();
|
||||||
@ -445,8 +459,9 @@ public final class GsonBuilder {
|
|||||||
|
|
||||||
JsonFormatter formatter = prettyPrinting ?
|
JsonFormatter formatter = prettyPrinting ?
|
||||||
new JsonPrintFormatter(escapeHtmlChars) : new JsonCompactFormatter(escapeHtmlChars);
|
new JsonPrintFormatter(escapeHtmlChars) : new JsonCompactFormatter(escapeHtmlChars);
|
||||||
Gson gson = new Gson(exclusionStrategy, fieldNamingPolicy, objConstructor,
|
Gson gson = new Gson(serializationExclusionStrategy, deserializationExclusionStrategy,
|
||||||
formatter, serializeNulls, customSerializers, customDeserializers, generateNonExecutableJson);
|
fieldNamingPolicy, objConstructor, formatter, serializeNulls, customSerializers,
|
||||||
|
customDeserializers, generateNonExecutableJson);
|
||||||
return gson;
|
return gson;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,8 +34,8 @@ import java.lang.annotation.Target;
|
|||||||
* <p><pre>
|
* <p><pre>
|
||||||
* public class User {
|
* public class User {
|
||||||
* @Expose private String firstName;
|
* @Expose private String firstName;
|
||||||
* @Expose private String lastName;
|
* @Expose(serialize = false) private String lastName;
|
||||||
* @Expose private String emailAddress;
|
* @Expose (serialize = false, deserialize = false) private String emailAddress;
|
||||||
* private String password;
|
* private String password;
|
||||||
* }
|
* }
|
||||||
* </pre></p>
|
* </pre></p>
|
||||||
@ -45,7 +45,9 @@ import java.lang.annotation.Target;
|
|||||||
* with {@code Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create()}
|
* with {@code Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create()}
|
||||||
* then the {@code toJson()} and {@code fromJson()} methods of Gson will exclude the
|
* 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 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
|
* <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
|
* {@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)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Target(ElementType.FIELD)
|
@Target(ElementType.FIELD)
|
||||||
public @interface Expose {
|
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;
|
package com.google.gson;
|
||||||
|
|
||||||
|
import com.google.gson.ExposeAnnotationBasedExclusionStrategy.Phase;
|
||||||
import com.google.gson.annotations.Expose;
|
import com.google.gson.annotations.Expose;
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
@ -33,7 +34,7 @@ public class ExposeAnnotationBasedExclusionStrategyTest extends TestCase {
|
|||||||
@Override
|
@Override
|
||||||
protected void setUp() throws Exception {
|
protected void setUp() throws Exception {
|
||||||
super.setUp();
|
super.setUp();
|
||||||
strategy = new ExposeAnnotationBasedExclusionStrategy();
|
strategy = new ExposeAnnotationBasedExclusionStrategy(Phase.SERIALIZATION);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testNeverSkipClasses() throws Exception {
|
public void testNeverSkipClasses() throws Exception {
|
||||||
|
@ -72,11 +72,12 @@ public class ExposeFieldsTest extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testExposeAnnotationDeserialization() throws Exception {
|
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);
|
ClassWithExposedFields target = gson.fromJson(json, ClassWithExposedFields.class);
|
||||||
|
|
||||||
assertEquals(3, (int) target.a);
|
assertEquals(3, (int) target.a);
|
||||||
assertNull(target.b);
|
assertNull(target.b);
|
||||||
|
assertFalse(target.d == 20);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testNoExposedFieldSerialization() throws Exception {
|
public void testNoExposedFieldSerialization() throws Exception {
|
||||||
@ -87,7 +88,7 @@ public class ExposeFieldsTest extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testNoExposedFieldDeserialization() throws Exception {
|
public void testNoExposedFieldDeserialization() throws Exception {
|
||||||
String json = '{' + "\"a\":" + 4 + ",\"b\":" + 5 + '}';
|
String json = "{a:4,b:5}";
|
||||||
ClassWithNoExposedFields obj = gson.fromJson(json, ClassWithNoExposedFields.class);
|
ClassWithNoExposedFields obj = gson.fromJson(json, ClassWithNoExposedFields.class);
|
||||||
|
|
||||||
assertEquals(0, obj.a);
|
assertEquals(0, obj.a);
|
||||||
@ -112,39 +113,37 @@ public class ExposeFieldsTest extends TestCase {
|
|||||||
private static class ClassWithExposedFields {
|
private static class ClassWithExposedFields {
|
||||||
@Expose private final Integer a;
|
@Expose private final Integer a;
|
||||||
private final Integer b;
|
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() {
|
ClassWithExposedFields() {
|
||||||
this(null, null);
|
this(null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ClassWithExposedFields(Integer a, Integer b) {
|
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.a = a;
|
||||||
this.b = b;
|
this.b = b;
|
||||||
|
this.c = c;
|
||||||
|
this.d = d;
|
||||||
|
this.e = e;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getExpectedJson() {
|
public String getExpectedJson() {
|
||||||
if (a == null) {
|
StringBuilder sb = new StringBuilder("{");
|
||||||
return "{}";
|
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() {
|
public String getExpectedJsonWithoutAnnotations() {
|
||||||
StringBuilder stringBuilder = new StringBuilder();
|
return String.format("{\"a\":%d,\"b\":%d,\"c\":%d,\"d\":%f,\"e\":\"%c\"}", a, b, c, d, e);
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -178,5 +177,5 @@ public class ExposeFieldsTest extends TestCase {
|
|||||||
public ClassWithInterfaceField(SomeInterface interfaceField) {
|
public ClassWithInterfaceField(SomeInterface interfaceField) {
|
||||||
this.interfaceField = interfaceField;
|
this.interfaceField = interfaceField;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user