Added special serialization of "Long". Now the client has the ability to output a long field as a JSON "String". This is useful for JavaScript clients that need to handle long values.
As well, this change does a major clean up of the custom type adapter handling and ParameterizedTypeMap creation.
This commit is contained in:
parent
859af0025c
commit
458f2baa2f
@ -53,7 +53,8 @@ import java.util.UUID;
|
||||
final class DefaultTypeAdapters {
|
||||
|
||||
private static final DefaultDateTypeAdapter DATE_TYPE_ADAPTER =
|
||||
new DefaultDateTypeAdapter(DateFormat.getDateTimeInstance());
|
||||
new DefaultDateTypeAdapter(DateFormat.getDateTimeInstance());
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static final EnumTypeAdapter ENUM_TYPE_ADAPTER = new EnumTypeAdapter();
|
||||
private static final UrlTypeAdapter URL_TYPE_ADAPTER = new UrlTypeAdapter();
|
||||
@ -68,10 +69,10 @@ final class DefaultTypeAdapters {
|
||||
private static final BooleanTypeAdapter BOOLEAN_TYPE_ADAPTER = new BooleanTypeAdapter();
|
||||
private static final ByteTypeAdapter BYTE_TYPE_ADAPTER = new ByteTypeAdapter();
|
||||
private static final CharacterTypeAdapter CHARACTER_TYPE_ADAPTER = new CharacterTypeAdapter();
|
||||
private static final DoubleTypeAdapter DOUBLE_TYPE_ADAPTER = new DoubleTypeAdapter();
|
||||
private static final DoubleDeserializer DOUBLE_TYPE_ADAPTER = new DoubleDeserializer();
|
||||
private static final FloatDeserializer FLOAT_TYPE_ADAPTER = new FloatDeserializer();
|
||||
private static final IntegerTypeAdapter INTEGER_TYPE_ADAPTER = new IntegerTypeAdapter();
|
||||
private static final LongTypeAdapter LONG_TYPE_ADAPTER = new LongTypeAdapter();
|
||||
private static final LongDeserializer LONG_DESERIALIZER = new LongDeserializer();
|
||||
private static final NumberTypeAdapter NUMBER_TYPE_ADAPTER = new NumberTypeAdapter();
|
||||
private static final ShortTypeAdapter SHORT_TYPE_ADAPTER = new ShortTypeAdapter();
|
||||
private static final StringTypeAdapter STRING_TYPE_ADAPTER = new StringTypeAdapter();
|
||||
@ -81,16 +82,16 @@ final class DefaultTypeAdapters {
|
||||
// The constants DEFAULT_SERIALIZERS, DEFAULT_DESERIALIZERS, and DEFAULT_INSTANCE_CREATORS
|
||||
// must be defined after the constants for the type adapters. Otherwise, the type adapter
|
||||
// constants will appear as nulls.
|
||||
static final ParameterizedTypeHandlerMap<JsonSerializer<?>> DEFAULT_SERIALIZERS =
|
||||
getDefaultSerializers();
|
||||
static final ParameterizedTypeHandlerMap<JsonDeserializer<?>> DEFAULT_DESERIALIZERS =
|
||||
getDefaultDeserializers();
|
||||
static final ParameterizedTypeHandlerMap<InstanceCreator<?>> DEFAULT_INSTANCE_CREATORS =
|
||||
getDefaultInstanceCreators();
|
||||
private static final ParameterizedTypeHandlerMap<JsonSerializer<?>> DEFAULT_SERIALIZERS =
|
||||
createDefaultSerializers();
|
||||
private static final ParameterizedTypeHandlerMap<JsonDeserializer<?>> DEFAULT_DESERIALIZERS =
|
||||
createDefaultDeserializers();
|
||||
private static final ParameterizedTypeHandlerMap<InstanceCreator<?>> DEFAULT_INSTANCE_CREATORS =
|
||||
createDefaultInstanceCreators();
|
||||
|
||||
private static ParameterizedTypeHandlerMap<JsonSerializer<?>> getDefaultSerializers() {
|
||||
private static ParameterizedTypeHandlerMap<JsonSerializer<?>> createDefaultSerializers() {
|
||||
ParameterizedTypeHandlerMap<JsonSerializer<?>> map =
|
||||
new ParameterizedTypeHandlerMap<JsonSerializer<?>>();
|
||||
new ParameterizedTypeHandlerMap<JsonSerializer<?>>();
|
||||
|
||||
map.register(Enum.class, ENUM_TYPE_ADAPTER);
|
||||
map.register(URL.class, URL_TYPE_ADAPTER);
|
||||
@ -112,8 +113,6 @@ final class DefaultTypeAdapters {
|
||||
map.register(char.class, CHARACTER_TYPE_ADAPTER);
|
||||
map.register(Integer.class, INTEGER_TYPE_ADAPTER);
|
||||
map.register(int.class, INTEGER_TYPE_ADAPTER);
|
||||
map.register(Long.class, LONG_TYPE_ADAPTER);
|
||||
map.register(long.class, LONG_TYPE_ADAPTER);
|
||||
map.register(Number.class, NUMBER_TYPE_ADAPTER);
|
||||
map.register(Short.class, SHORT_TYPE_ADAPTER);
|
||||
map.register(short.class, SHORT_TYPE_ADAPTER);
|
||||
@ -123,9 +122,9 @@ final class DefaultTypeAdapters {
|
||||
return map;
|
||||
}
|
||||
|
||||
private static ParameterizedTypeHandlerMap<JsonDeserializer<?>> getDefaultDeserializers() {
|
||||
private static ParameterizedTypeHandlerMap<JsonDeserializer<?>> createDefaultDeserializers() {
|
||||
ParameterizedTypeHandlerMap<JsonDeserializer<?>> map =
|
||||
new ParameterizedTypeHandlerMap<JsonDeserializer<?>>();
|
||||
new ParameterizedTypeHandlerMap<JsonDeserializer<?>>();
|
||||
map.register(Enum.class, wrapDeserializer(ENUM_TYPE_ADAPTER));
|
||||
map.register(URL.class, wrapDeserializer(URL_TYPE_ADAPTER));
|
||||
map.register(URI.class, wrapDeserializer(URI_TYPE_ADAPTER));
|
||||
@ -150,8 +149,8 @@ final class DefaultTypeAdapters {
|
||||
map.register(float.class, wrapDeserializer(FLOAT_TYPE_ADAPTER));
|
||||
map.register(Integer.class, wrapDeserializer(INTEGER_TYPE_ADAPTER));
|
||||
map.register(int.class, wrapDeserializer(INTEGER_TYPE_ADAPTER));
|
||||
map.register(Long.class, wrapDeserializer(LONG_TYPE_ADAPTER));
|
||||
map.register(long.class, wrapDeserializer(LONG_TYPE_ADAPTER));
|
||||
map.register(Long.class, wrapDeserializer(LONG_DESERIALIZER));
|
||||
map.register(long.class, wrapDeserializer(LONG_DESERIALIZER));
|
||||
map.register(Number.class, wrapDeserializer(NUMBER_TYPE_ADAPTER));
|
||||
map.register(Short.class, wrapDeserializer(SHORT_TYPE_ADAPTER));
|
||||
map.register(short.class, wrapDeserializer(SHORT_TYPE_ADAPTER));
|
||||
@ -161,7 +160,7 @@ final class DefaultTypeAdapters {
|
||||
return map;
|
||||
}
|
||||
|
||||
private static ParameterizedTypeHandlerMap<InstanceCreator<?>> getDefaultInstanceCreators() {
|
||||
private static ParameterizedTypeHandlerMap<InstanceCreator<?>> createDefaultInstanceCreators() {
|
||||
ParameterizedTypeHandlerMap<InstanceCreator<?>> map =
|
||||
new ParameterizedTypeHandlerMap<InstanceCreator<?>>();
|
||||
map.register(Enum.class, ENUM_TYPE_ADAPTER);
|
||||
@ -183,16 +182,43 @@ final class DefaultTypeAdapters {
|
||||
return new JsonDeserializerExceptionWrapper(deserializer);
|
||||
}
|
||||
|
||||
static void registerSerializersForFloatingPoints(boolean serializeSpecialFloatingPointValues,
|
||||
ParameterizedTypeHandlerMap<JsonSerializer<?>> serializers) {
|
||||
static ParameterizedTypeHandlerMap<JsonSerializer<?>> getDefaultSerializers() {
|
||||
return getDefaultSerializers(false, false);
|
||||
}
|
||||
|
||||
static ParameterizedTypeHandlerMap<JsonSerializer<?>> getDefaultSerializers(
|
||||
boolean serializeSpecialFloatingPointValues, boolean serializeLongsAsString) {
|
||||
ParameterizedTypeHandlerMap<JsonSerializer<?>> serializers =
|
||||
new ParameterizedTypeHandlerMap<JsonSerializer<?>>();
|
||||
|
||||
// Double primitive
|
||||
DefaultTypeAdapters.DoubleSerializer doubleSerializer =
|
||||
new DefaultTypeAdapters.DoubleSerializer(serializeSpecialFloatingPointValues);
|
||||
DefaultTypeAdapters.FloatSerializer floatSerializer =
|
||||
new DefaultTypeAdapters.FloatSerializer(serializeSpecialFloatingPointValues);
|
||||
serializers.registerIfAbsent(Double.class, doubleSerializer);
|
||||
serializers.registerIfAbsent(double.class, doubleSerializer);
|
||||
|
||||
// Float primitive
|
||||
DefaultTypeAdapters.FloatSerializer floatSerializer =
|
||||
new DefaultTypeAdapters.FloatSerializer(serializeSpecialFloatingPointValues);
|
||||
serializers.registerIfAbsent(Float.class, floatSerializer);
|
||||
serializers.registerIfAbsent(float.class, floatSerializer);
|
||||
|
||||
// Long primitive
|
||||
DefaultTypeAdapters.LongSerializer longSerializer =
|
||||
new DefaultTypeAdapters.LongSerializer(serializeLongsAsString);
|
||||
serializers.registerIfAbsent(Long.class, longSerializer);
|
||||
serializers.registerIfAbsent(long.class, longSerializer);
|
||||
|
||||
serializers.registerIfAbsent(DEFAULT_SERIALIZERS);
|
||||
return serializers;
|
||||
}
|
||||
|
||||
static ParameterizedTypeHandlerMap<JsonDeserializer<?>> getDefaultDeserializers() {
|
||||
return DEFAULT_DESERIALIZERS;
|
||||
}
|
||||
|
||||
static ParameterizedTypeHandlerMap<InstanceCreator<?>> getDefaultInstanceCreators() {
|
||||
return DEFAULT_INSTANCE_CREATORS;
|
||||
}
|
||||
|
||||
static class DefaultDateTypeAdapter implements JsonSerializer<Date>, JsonDeserializer<Date> {
|
||||
@ -523,15 +549,34 @@ final class DefaultTypeAdapters {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return LongTypeAdapter.class.getSimpleName();
|
||||
return NumberTypeAdapter.class.getSimpleName();
|
||||
}
|
||||
}
|
||||
|
||||
private static class LongSerializer implements JsonSerializer<Long> {
|
||||
private final boolean serializeAsString;
|
||||
|
||||
private LongSerializer(boolean serializeAsString) {
|
||||
this.serializeAsString = serializeAsString;
|
||||
}
|
||||
|
||||
public JsonElement serialize(Long src, Type typeOfSrc, JsonSerializationContext context) {
|
||||
if (src == null) {
|
||||
return JsonNull.createJsonNull();
|
||||
} else if (serializeAsString) {
|
||||
return new JsonPrimitive(String.valueOf(src));
|
||||
} else {
|
||||
return new JsonPrimitive(src);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return LongSerializer.class.getSimpleName();
|
||||
}
|
||||
}
|
||||
|
||||
private static class LongTypeAdapter implements JsonSerializer<Long>, JsonDeserializer<Long> {
|
||||
public JsonElement serialize(Long src, Type typeOfSrc, JsonSerializationContext context) {
|
||||
return new JsonPrimitive(src);
|
||||
}
|
||||
|
||||
private static class LongDeserializer implements JsonDeserializer<Long> {
|
||||
public Long deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
|
||||
throws JsonParseException {
|
||||
return json.getAsLong();
|
||||
@ -539,7 +584,7 @@ final class DefaultTypeAdapters {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return LongTypeAdapter.class.getSimpleName();
|
||||
return LongDeserializer.class.getSimpleName();
|
||||
}
|
||||
}
|
||||
|
||||
@ -643,7 +688,7 @@ final class DefaultTypeAdapters {
|
||||
}
|
||||
}
|
||||
|
||||
private static class DoubleTypeAdapter implements JsonDeserializer<Double> {
|
||||
private static class DoubleDeserializer implements JsonDeserializer<Double> {
|
||||
public Double deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
|
||||
throws JsonParseException {
|
||||
return json.getAsDouble();
|
||||
@ -651,7 +696,7 @@ final class DefaultTypeAdapters {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return DoubleTypeAdapter.class.getSimpleName();
|
||||
return DoubleDeserializer.class.getSimpleName();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -142,9 +142,9 @@ public final class Gson {
|
||||
*/
|
||||
Gson(ExclusionStrategy strategy, FieldNamingStrategy fieldNamingPolicy) {
|
||||
this(strategy, fieldNamingPolicy,
|
||||
createObjectConstructor(DefaultTypeAdapters.DEFAULT_INSTANCE_CREATORS),
|
||||
DEFAULT_JSON_FORMATTER, false,
|
||||
getDefaultSerializers(), DefaultTypeAdapters.DEFAULT_DESERIALIZERS);
|
||||
createObjectConstructor(DefaultTypeAdapters.getDefaultInstanceCreators()),
|
||||
DEFAULT_JSON_FORMATTER, false, DefaultTypeAdapters.getDefaultSerializers(),
|
||||
DefaultTypeAdapters.getDefaultDeserializers());
|
||||
}
|
||||
|
||||
Gson(ExclusionStrategy strategy, FieldNamingStrategy fieldNamingPolicy,
|
||||
@ -159,13 +159,6 @@ public final class Gson {
|
||||
this.serializers = serializers;
|
||||
this.deserializers = deserializers;
|
||||
}
|
||||
|
||||
private static ParameterizedTypeHandlerMap<JsonSerializer<?>> getDefaultSerializers() {
|
||||
ParameterizedTypeHandlerMap<JsonSerializer<?>> serializers =
|
||||
DefaultTypeAdapters.DEFAULT_SERIALIZERS.copyOf();
|
||||
DefaultTypeAdapters.registerSerializersForFloatingPoints(false, serializers);
|
||||
return serializers;
|
||||
}
|
||||
|
||||
static MappedObjectConstructor createObjectConstructor(
|
||||
ParameterizedTypeHandlerMap<InstanceCreator<?>> instanceCreators) {
|
||||
|
@ -52,6 +52,7 @@ import java.util.List;
|
||||
public final class GsonBuilder {
|
||||
|
||||
private double ignoreVersionsAfter;
|
||||
private boolean serializeLongAsString;
|
||||
private ModifierBasedExclusionStrategy modifierBasedExclusionStrategy;
|
||||
private boolean serializeInnerClasses;
|
||||
private final AnonymousAndLocalClassExclusionStrategy anonAndLocalClassExclusionStrategy;
|
||||
@ -77,6 +78,7 @@ public final class GsonBuilder {
|
||||
public GsonBuilder() {
|
||||
// setup default values
|
||||
ignoreVersionsAfter = VersionConstants.IGNORE_VERSIONS;
|
||||
serializeLongAsString = false;
|
||||
serializeInnerClasses = true;
|
||||
anonAndLocalClassExclusionStrategy = new AnonymousAndLocalClassExclusionStrategy();
|
||||
innerClassExclusionStrategy = new InnerClassExclusionStrategy();
|
||||
@ -146,7 +148,19 @@ public final class GsonBuilder {
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures Gson to include or exclude inner classes
|
||||
* Configures Gson to output fields of type {@code long} as {@code String}s instead of a number.
|
||||
*
|
||||
* @param value the boolean value on whether or not {@code Gson} should serialize a {@code long}
|
||||
* field as a {@code String}
|
||||
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
|
||||
*/
|
||||
public GsonBuilder serializeLongFieldsAsString(boolean value) {
|
||||
serializeLongAsString = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures Gson to include or exclude inner classes.
|
||||
*
|
||||
* @param value the boolean value on whether or not {@code Gson} should serialize inner classes
|
||||
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
|
||||
@ -389,24 +403,24 @@ public final class GsonBuilder {
|
||||
|
||||
ParameterizedTypeHandlerMap<JsonSerializer<?>> customSerializers = serializers.copyOf();
|
||||
ParameterizedTypeHandlerMap<JsonDeserializer<?>> customDeserializers = deserializers.copyOf();
|
||||
|
||||
addTypeAdaptersForDate(datePattern, dateStyle, timeStyle, customSerializers,
|
||||
addTypeAdaptersForDate(datePattern, dateStyle, timeStyle, customSerializers,
|
||||
customDeserializers);
|
||||
customSerializers.registerIfAbsent(DefaultTypeAdapters.DEFAULT_SERIALIZERS);
|
||||
DefaultTypeAdapters.registerSerializersForFloatingPoints(serializeSpecialFloatingPointValues,
|
||||
customSerializers);
|
||||
customDeserializers.registerIfAbsent(DefaultTypeAdapters.DEFAULT_DESERIALIZERS);
|
||||
|
||||
customSerializers.registerIfAbsent(DefaultTypeAdapters.getDefaultSerializers(
|
||||
serializeSpecialFloatingPointValues, serializeLongAsString));
|
||||
|
||||
customDeserializers.registerIfAbsent(DefaultTypeAdapters.getDefaultDeserializers());
|
||||
|
||||
ParameterizedTypeHandlerMap<InstanceCreator<?>> customInstanceCreators =
|
||||
instanceCreators.copyOf();
|
||||
customInstanceCreators.registerIfAbsent(DefaultTypeAdapters.DEFAULT_INSTANCE_CREATORS);
|
||||
customInstanceCreators.registerIfAbsent(DefaultTypeAdapters.getDefaultInstanceCreators());
|
||||
MappedObjectConstructor objConstructor = Gson.createObjectConstructor(customInstanceCreators);
|
||||
|
||||
Gson gson = new Gson(exclusionStrategy, fieldNamingPolicy, objConstructor,
|
||||
formatter, serializeNulls, customSerializers, customDeserializers);
|
||||
return gson;
|
||||
}
|
||||
|
||||
|
||||
private static void addTypeAdaptersForDate(String datePattern, int dateStyle, int timeStyle,
|
||||
ParameterizedTypeHandlerMap<JsonSerializer<?>> serializers,
|
||||
ParameterizedTypeHandlerMap<JsonDeserializer<?>> deserializers) {
|
||||
|
@ -73,13 +73,13 @@ public class ParameterizedTypesTest extends TestCase {
|
||||
|
||||
public void testTypesWithMultipleParametersSerialization() throws Exception {
|
||||
MultiParameters<Integer, Float, Double, String, BagOfPrimitives> src =
|
||||
new MultiParameters<Integer, Float, Double, String, BagOfPrimitives>(10, 1.0F, 2.1D,
|
||||
"abc", new BagOfPrimitives());
|
||||
new MultiParameters<Integer, Float, Double, String, BagOfPrimitives>(10, 1.0F, 2.1D,
|
||||
"abc", new BagOfPrimitives());
|
||||
Type typeOfSrc = new TypeToken<MultiParameters<Integer, Float, Double, String,
|
||||
BagOfPrimitives>>() {}.getType();
|
||||
String json = gson.toJson(src, typeOfSrc);
|
||||
String expected = "{\"a\":10,\"b\":1.0,\"c\":2.1,\"d\":\"abc\","
|
||||
+ "\"e\":{\"longValue\":0,\"intValue\":0,\"booleanValue\":false,\"stringValue\":\"\"}}";
|
||||
+ "\"e\":{\"longValue\":0,\"intValue\":0,\"booleanValue\":false,\"stringValue\":\"\"}}";
|
||||
assertEquals(expected, json);
|
||||
}
|
||||
|
||||
@ -87,12 +87,12 @@ public class ParameterizedTypesTest extends TestCase {
|
||||
Type typeOfTarget = new TypeToken<MultiParameters<Integer, Float, Double, String,
|
||||
BagOfPrimitives>>() {}.getType();
|
||||
String json = "{\"a\":10,\"b\":1.0,\"c\":2.1,\"d\":\"abc\","
|
||||
+ "\"e\":{\"longValue\":0,\"intValue\":0,\"booleanValue\":false,\"stringValue\":\"\"}}";
|
||||
+ "\"e\":{\"longValue\":0,\"intValue\":0,\"booleanValue\":false,\"stringValue\":\"\"}}";
|
||||
MultiParameters<Integer, Float, Double, String, BagOfPrimitives> target =
|
||||
gson.fromJson(json, typeOfTarget);
|
||||
gson.fromJson(json, typeOfTarget);
|
||||
MultiParameters<Integer, Float, Double, String, BagOfPrimitives> expected =
|
||||
new MultiParameters<Integer, Float, Double, String, BagOfPrimitives>(10, 1.0F, 2.1D,
|
||||
"abc", new BagOfPrimitives());
|
||||
new MultiParameters<Integer, Float, Double, String, BagOfPrimitives>(10, 1.0F, 2.1D,
|
||||
"abc", new BagOfPrimitives());
|
||||
assertEquals(expected, target);
|
||||
}
|
||||
|
||||
|
@ -528,4 +528,23 @@ public class PrimitiveTest extends TestCase {
|
||||
} catch (JsonParseException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
public void testLongAsStringSerialization() throws Exception {
|
||||
gson = new GsonBuilder().serializeLongFieldsAsString(true).create();
|
||||
String result = gson.toJson(15L);
|
||||
assertEquals("\"15\"", result);
|
||||
|
||||
// Test with an integer and ensure its still a number
|
||||
result = gson.toJson(2);
|
||||
assertEquals("2", result);
|
||||
}
|
||||
|
||||
public void testLongAsStringDeserialization() throws Exception {
|
||||
long value = gson.fromJson("\"15\"", long.class);
|
||||
assertEquals(15, value);
|
||||
|
||||
gson = new GsonBuilder().serializeLongFieldsAsString(true).create();
|
||||
value = gson.fromJson("\"25\"", long.class);
|
||||
assertEquals(25, value);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user