Added a GsonBuilder setting to enable serialization of special double types, NaN, Infinity, and -Infinity.
This commit is contained in:
parent
362a94ec74
commit
51881c7f4a
@ -111,10 +111,6 @@ final class DefaultTypeAdapters {
|
||||
map.register(byte.class, BYTE_TYPE_ADAPTER);
|
||||
map.register(Character.class, CHARACTER_TYPE_ADAPTER);
|
||||
map.register(char.class, CHARACTER_TYPE_ADAPTER);
|
||||
map.register(Double.class, DOUBLE_TYPE_ADAPTER);
|
||||
map.register(double.class, DOUBLE_TYPE_ADAPTER);
|
||||
map.register(Float.class, FLOAT_TYPE_ADAPTER);
|
||||
map.register(float.class, FLOAT_TYPE_ADAPTER);
|
||||
map.register(Integer.class, INTEGER_TYPE_ADAPTER);
|
||||
map.register(int.class, INTEGER_TYPE_ADAPTER);
|
||||
map.register(Long.class, LONG_TYPE_ADAPTER);
|
||||
@ -211,6 +207,18 @@ final class DefaultTypeAdapters {
|
||||
return new JsonDeserializerExceptionWrapper(deserializer);
|
||||
}
|
||||
|
||||
static void registerSerializersForFloatingPoints(boolean serializeSpecialFloatingPointValues,
|
||||
ParameterizedTypeHandlerMap<JsonSerializer<?>> serializers) {
|
||||
DefaultTypeAdapters.DoubleSerializer doubleSerializer =
|
||||
new DefaultTypeAdapters.DoubleSerializer(serializeSpecialFloatingPointValues);
|
||||
DefaultTypeAdapters.FloatSerializer floatSerializer =
|
||||
new DefaultTypeAdapters.FloatSerializer(serializeSpecialFloatingPointValues);
|
||||
serializers.registerIfAbsent(Double.class, doubleSerializer);
|
||||
serializers.registerIfAbsent(double.class, doubleSerializer);
|
||||
serializers.registerIfAbsent(Float.class, floatSerializer);
|
||||
serializers.registerIfAbsent(float.class, floatSerializer);
|
||||
}
|
||||
|
||||
static class DefaultDateTypeAdapter implements JsonSerializer<Date>, JsonDeserializer<Date> {
|
||||
|
||||
private final DateFormat format;
|
||||
@ -640,14 +648,26 @@ final class DefaultTypeAdapters {
|
||||
}
|
||||
}
|
||||
|
||||
private static class FloatTypeAdapter
|
||||
implements InstanceCreator<Float>, JsonSerializer<Float>, JsonDeserializer<Float> {
|
||||
static class FloatSerializer implements JsonSerializer<Float> {
|
||||
private final boolean serializeSpecialFloatingPointValues;
|
||||
|
||||
FloatSerializer(boolean serializeSpecialDoubleValues) {
|
||||
this.serializeSpecialFloatingPointValues = serializeSpecialDoubleValues;
|
||||
}
|
||||
|
||||
public JsonElement serialize(Float src, Type typeOfSrc, JsonSerializationContext context) {
|
||||
if (Float.isNaN(src) || Float.isInfinite(src)) {
|
||||
throw new IllegalArgumentException(src + " is not a valid double value as per JavaScript specification.");
|
||||
if (!serializeSpecialFloatingPointValues) {
|
||||
if (Float.isNaN(src) || Float.isInfinite(src)) {
|
||||
throw new IllegalArgumentException(src
|
||||
+ " is not a valid float value as per JSON specification. To override this"
|
||||
+ " behavior, use GsonBuilder.serializeSpecialFloatingPointValues() method.");
|
||||
}
|
||||
}
|
||||
return new JsonPrimitive(src);
|
||||
}
|
||||
}
|
||||
|
||||
private static class FloatTypeAdapter implements InstanceCreator<Float>, JsonDeserializer<Float> {
|
||||
|
||||
public Float deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
|
||||
throws JsonParseException {
|
||||
@ -664,15 +684,27 @@ final class DefaultTypeAdapters {
|
||||
}
|
||||
}
|
||||
|
||||
private static class DoubleTypeAdapter
|
||||
implements InstanceCreator<Double>, JsonSerializer<Double>, JsonDeserializer<Double> {
|
||||
static class DoubleSerializer implements JsonSerializer<Double> {
|
||||
private final boolean serializeSpecialFloatingPointValues;
|
||||
|
||||
DoubleSerializer(boolean serializeSpecialDoubleValues) {
|
||||
this.serializeSpecialFloatingPointValues = serializeSpecialDoubleValues;
|
||||
}
|
||||
public JsonElement serialize(Double src, Type typeOfSrc, JsonSerializationContext context) {
|
||||
if (Double.isNaN(src) || Double.isInfinite(src)) {
|
||||
throw new IllegalArgumentException(src + " is not a valid double value as per JavaScript specification.");
|
||||
if (!serializeSpecialFloatingPointValues) {
|
||||
if (Double.isNaN(src) || Double.isInfinite(src)) {
|
||||
throw new IllegalArgumentException(src
|
||||
+ " is not a valid double value as per JSON specification. To override this"
|
||||
+ " behavior, use GsonBuilder.serializeSpecialDoubleValues() method.");
|
||||
}
|
||||
}
|
||||
return new JsonPrimitive(src);
|
||||
}
|
||||
}
|
||||
|
||||
private static class DoubleTypeAdapter implements InstanceCreator<Double>,
|
||||
JsonDeserializer<Double> {
|
||||
|
||||
public Double deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
|
||||
throws JsonParseException {
|
||||
return json.getAsDouble();
|
||||
|
@ -141,9 +141,10 @@ public final class Gson {
|
||||
* encountering inner class references.
|
||||
*/
|
||||
Gson(ExclusionStrategy strategy, FieldNamingStrategy fieldNamingPolicy) {
|
||||
this(strategy, fieldNamingPolicy, createObjectConstructor(DefaultTypeAdapters.DEFAULT_INSTANCE_CREATORS),
|
||||
this(strategy, fieldNamingPolicy,
|
||||
createObjectConstructor(DefaultTypeAdapters.DEFAULT_INSTANCE_CREATORS),
|
||||
DEFAULT_JSON_FORMATTER, false,
|
||||
DefaultTypeAdapters.DEFAULT_SERIALIZERS, DefaultTypeAdapters.DEFAULT_DESERIALIZERS);
|
||||
getDefaultSerializers(), DefaultTypeAdapters.DEFAULT_DESERIALIZERS);
|
||||
}
|
||||
|
||||
Gson(ExclusionStrategy strategy, FieldNamingStrategy fieldNamingPolicy,
|
||||
@ -159,6 +160,13 @@ public final class Gson {
|
||||
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) {
|
||||
MappedObjectConstructor objectConstructor = new MappedObjectConstructor();
|
||||
|
@ -64,6 +64,7 @@ public final class GsonBuilder {
|
||||
private String datePattern;
|
||||
private int dateStyle;
|
||||
private int timeStyle;
|
||||
private boolean serializeSpecialFloatingPointValues;
|
||||
|
||||
/**
|
||||
* Creates a GsonBuilder instance that can be used to build Gson with various configuration
|
||||
@ -85,6 +86,7 @@ public final class GsonBuilder {
|
||||
serializeNulls = false;
|
||||
dateStyle = DateFormat.DEFAULT;
|
||||
timeStyle = DateFormat.DEFAULT;
|
||||
serializeSpecialFloatingPointValues = false;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -322,6 +324,31 @@ public final class GsonBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Section 2.4 of <a href="http://www.ietf.org/rfc/rfc4627.txt">JSON specification</a> disallows
|
||||
* special double values (NaN, Infinity, -Infinity). However,
|
||||
* <a href="http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf">Javascript
|
||||
* specification</a> (see section 4.3.20, 4.3.22, 4.3.23) allows these values as valid Javascript
|
||||
* values. Moreover, most JavaScript engines will accept these special values in JSON without
|
||||
* problem. So, at a practical level, it makes sense to accept these values as valid JSON even
|
||||
* though JSON specification disallows them.
|
||||
*
|
||||
* <p>Gson always accepts these special values during deserialization. However, it outputs
|
||||
* strictly compliant JSON. Hence, if it encounters a float value {@link Float.NaN},
|
||||
* {@link Float.POSITIVE_INFINITY}, {@link Float.NEGATIVE_INFINITY}, or a double value
|
||||
* {@link Double.NaN}, {@link Double.POSITIVE_INFINITY}, {@link Double.NEGATIVE_INFINITY}, it
|
||||
* will throw an {@link IllegalArgumentException}. This method provides a way to override the
|
||||
* default behavior when you know that the JSON receiver will be able to handle these special
|
||||
* values.
|
||||
*
|
||||
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
|
||||
* @Since 1.3
|
||||
*/
|
||||
public GsonBuilder serializeSpecialFloatingPointValues() {
|
||||
this.serializeSpecialFloatingPointValues = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link Gson} instance based on the current configuration. This method is free of
|
||||
* side-effects to this {@code GsonBuilder} instance and hence can be called multiple times.
|
||||
@ -346,8 +373,10 @@ public final class GsonBuilder {
|
||||
addTypeAdaptersForDate(datePattern, dateStyle, timeStyle, customSerializers,
|
||||
customDeserializers);
|
||||
customSerializers.registerIfAbsent(DefaultTypeAdapters.DEFAULT_SERIALIZERS);
|
||||
DefaultTypeAdapters.registerSerializersForFloatingPoints(serializeSpecialFloatingPointValues,
|
||||
customSerializers);
|
||||
customDeserializers.registerIfAbsent(DefaultTypeAdapters.DEFAULT_DESERIALIZERS);
|
||||
|
||||
|
||||
ParameterizedTypeHandlerMap<InstanceCreator<?>> customInstanceCreators =
|
||||
instanceCreators.copyOf();
|
||||
customInstanceCreators.registerIfAbsent(DefaultTypeAdapters.DEFAULT_INSTANCE_CREATORS);
|
||||
@ -357,7 +386,7 @@ public final class GsonBuilder {
|
||||
formatter, serializeNulls, customSerializers, customDeserializers);
|
||||
return gson;
|
||||
}
|
||||
|
||||
|
||||
private static void addTypeAdaptersForDate(String datePattern, int dateStyle, int timeStyle,
|
||||
ParameterizedTypeHandlerMap<JsonSerializer<?>> serializers,
|
||||
ParameterizedTypeHandlerMap<JsonDeserializer<?>> deserializers) {
|
||||
|
@ -58,6 +58,15 @@ final class ParameterizedTypeHandlerMap<T> {
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void registerIfAbsent(Type typeOfT, T value) {
|
||||
if (!modifiable) {
|
||||
throw new IllegalStateException("Attempted to modify an unmodifiable map.");
|
||||
}
|
||||
if (!map.containsKey(typeOfT)) {
|
||||
register(typeOfT, value);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void makeUnmodifiable() {
|
||||
modifiable = false;
|
||||
}
|
||||
|
@ -349,8 +349,8 @@ public class PrimitiveTest extends TestCase {
|
||||
return json.substring(json.indexOf('[') + 1, json.indexOf(']'));
|
||||
}
|
||||
|
||||
public void testDoubleNaNSerializationNotSupported() {
|
||||
double nan = (double) Double.NaN;
|
||||
public void testDoubleNaNSerializationNotSupportedByDefault() {
|
||||
double nan = Double.NaN;
|
||||
try {
|
||||
gson.toJson(nan);
|
||||
gson.toJson(Double.NaN);
|
||||
@ -358,13 +358,21 @@ public class PrimitiveTest extends TestCase {
|
||||
} catch (IllegalArgumentException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
public void testDoubleNaNSerialization() {
|
||||
Gson gson = new GsonBuilder().serializeSpecialFloatingPointValues().create();
|
||||
double nan = Double.NaN;
|
||||
assertEquals("NaN", gson.toJson(nan));
|
||||
assertEquals("NaN", gson.toJson(Double.NaN));
|
||||
}
|
||||
|
||||
public void testDoubleNaNDeserializationNotSupported() {
|
||||
public void testDoubleNaNDeserialization() {
|
||||
assertTrue(Double.isNaN(gson.fromJson("NaN", Double.class)));
|
||||
assertTrue(Double.isNaN(gson.fromJson("NaN", double.class)));
|
||||
}
|
||||
public void testFloatNaNSerializationNotSupported() {
|
||||
float nan = (float) Float.NaN;
|
||||
|
||||
public void testFloatNaNSerializationNotSupportedByDefault() {
|
||||
float nan = Float.NaN;
|
||||
try {
|
||||
gson.toJson(nan);
|
||||
gson.toJson(Float.NaN);
|
||||
@ -373,7 +381,14 @@ public class PrimitiveTest extends TestCase {
|
||||
}
|
||||
}
|
||||
|
||||
public void testFloatNaNDeserializationNotSupported() {
|
||||
public void testFloatNaNSerialization() {
|
||||
Gson gson = new GsonBuilder().serializeSpecialFloatingPointValues().create();
|
||||
float nan = Float.NaN;
|
||||
assertEquals("NaN", gson.toJson(nan));
|
||||
assertEquals("NaN", gson.toJson(Float.NaN));
|
||||
}
|
||||
|
||||
public void testFloatNaNDeserialization() {
|
||||
assertTrue(Float.isNaN(gson.fromJson("NaN", Float.class)));
|
||||
assertTrue(Float.isNaN(gson.fromJson("NaN", float.class)));
|
||||
}
|
||||
@ -381,37 +396,51 @@ public class PrimitiveTest extends TestCase {
|
||||
public void testBigDecimalNaNDeserializationNotSupported() {
|
||||
try {
|
||||
gson.fromJson("NaN", BigDecimal.class);
|
||||
fail("Gson should not accept NaN for deserialization");
|
||||
fail("Gson should not accept NaN for deserialization by default.");
|
||||
} catch (JsonParseException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
public void testDoubleInfinitySerializationNotSupported() {
|
||||
double infinity = (double)Double.POSITIVE_INFINITY;
|
||||
public void testDoubleInfinitySerializationNotSupportedByDefault() {
|
||||
double infinity = Double.POSITIVE_INFINITY;
|
||||
try {
|
||||
gson.toJson(infinity);
|
||||
gson.toJson(Double.POSITIVE_INFINITY);
|
||||
fail("Gson should not accept positive infinity for serialization");
|
||||
fail("Gson should not accept positive infinity for serialization by default.");
|
||||
} catch (IllegalArgumentException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
public void testDoubleInfinityDeserializationNotSupported() {
|
||||
public void testDoubleInfinitySerialization() {
|
||||
Gson gson = new GsonBuilder().serializeSpecialFloatingPointValues().create();
|
||||
double infinity = Double.POSITIVE_INFINITY;
|
||||
assertEquals("Infinity", gson.toJson(infinity));
|
||||
assertEquals("Infinity", gson.toJson(Double.POSITIVE_INFINITY));
|
||||
}
|
||||
|
||||
public void testDoubleInfinityDeserialization() {
|
||||
assertTrue(Double.isInfinite(gson.fromJson("Infinity", Double.class)));
|
||||
assertTrue(Double.isInfinite(gson.fromJson("Infinity", double.class)));
|
||||
}
|
||||
|
||||
public void testFloatInfinitySerializationNotSupported() {
|
||||
float infinity = (float) Float.POSITIVE_INFINITY;
|
||||
public void testFloatInfinitySerializationNotSupportedByDefault() {
|
||||
float infinity = Float.POSITIVE_INFINITY;
|
||||
try {
|
||||
gson.toJson(infinity);
|
||||
gson.toJson(Float.POSITIVE_INFINITY);
|
||||
fail("Gson should not accept positive infinity for serialization");
|
||||
fail("Gson should not accept positive infinity for serialization by default");
|
||||
} catch (IllegalArgumentException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
public void testFloatInfinityDeserializationNotSupported() {
|
||||
public void testFloatInfinitySerialization() {
|
||||
Gson gson = new GsonBuilder().serializeSpecialFloatingPointValues().create();
|
||||
float infinity = Float.POSITIVE_INFINITY;
|
||||
assertEquals("Infinity", gson.toJson(infinity));
|
||||
assertEquals("Infinity", gson.toJson(Float.POSITIVE_INFINITY));
|
||||
}
|
||||
|
||||
public void testFloatInfinityDeserialization() {
|
||||
assertTrue(Float.isInfinite(gson.fromJson("Infinity", Float.class)));
|
||||
assertTrue(Float.isInfinite(gson.fromJson("Infinity", float.class)));
|
||||
}
|
||||
@ -419,46 +448,60 @@ public class PrimitiveTest extends TestCase {
|
||||
public void testBigDecimalInfinityDeserializationNotSupported() {
|
||||
try {
|
||||
gson.fromJson("Infinity", BigDecimal.class);
|
||||
fail("Gson should not accept positive infinity for deserialization");
|
||||
fail("Gson should not accept positive infinity for deserialization with BigDecimal");
|
||||
} catch (JsonParseException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
public void testNegativeInfinitySerializationNotSupported() {
|
||||
double negativeInfinity = (double)Double.NEGATIVE_INFINITY;
|
||||
public void testNegativeInfinitySerializationNotSupportedByDefault() {
|
||||
double negativeInfinity = Double.NEGATIVE_INFINITY;
|
||||
try {
|
||||
gson.toJson(negativeInfinity);
|
||||
gson.toJson(Double.NEGATIVE_INFINITY);
|
||||
fail("Gson should not accept positive infinity for serialization");
|
||||
fail("Gson should not accept negative infinity for serialization by default");
|
||||
} catch (IllegalArgumentException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
public void testNegativeInfinityDeserializationNotSupported() {
|
||||
public void testNegativeInfinitySerialization() {
|
||||
Gson gson = new GsonBuilder().serializeSpecialFloatingPointValues().create();
|
||||
double negativeInfinity = Double.NEGATIVE_INFINITY;
|
||||
assertEquals("-Infinity", gson.toJson(negativeInfinity));
|
||||
assertEquals("-Infinity", gson.toJson(Double.NEGATIVE_INFINITY));
|
||||
}
|
||||
|
||||
public void testNegativeInfinityDeserialization() {
|
||||
assertTrue(Double.isInfinite(gson.fromJson("-Infinity", double.class)));
|
||||
assertTrue(Double.isInfinite(gson.fromJson("-Infinity", Double.class)));
|
||||
}
|
||||
|
||||
public void testNegativeInfinityFloatSerializationNotSupported() {
|
||||
float negativeInfinity = (float) Float.NEGATIVE_INFINITY;
|
||||
public void testNegativeInfinityFloatSerializationNotSupportedByDefault() {
|
||||
float negativeInfinity = Float.NEGATIVE_INFINITY;
|
||||
try {
|
||||
gson.toJson(negativeInfinity);
|
||||
gson.toJson(Float.NEGATIVE_INFINITY);
|
||||
fail("Gson should not accept positive infinity for serialization");
|
||||
fail("Gson should not accept negative infinity for serialization by default");
|
||||
} catch (IllegalArgumentException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
public void testNegativeInfinityFloatDeserializationNotSupported() {
|
||||
public void testNegativeInfinityFloatSerialization() {
|
||||
Gson gson = new GsonBuilder().serializeSpecialFloatingPointValues().create();
|
||||
float negativeInfinity = Float.NEGATIVE_INFINITY;
|
||||
assertEquals("-Infinity", gson.toJson(negativeInfinity));
|
||||
assertEquals("-Infinity", gson.toJson(Float.NEGATIVE_INFINITY));
|
||||
}
|
||||
|
||||
public void testNegativeInfinityFloatDeserialization() {
|
||||
assertTrue(Float.isInfinite(gson.fromJson("-Infinity", float.class)));
|
||||
assertTrue(Float.isInfinite(gson.fromJson("-Infinity", Float.class)));
|
||||
}
|
||||
|
||||
public void testNegativeInfinityBigDecimalDeserializationNotSupported() {
|
||||
public void testBigDecimalNegativeInfinityDeserializationNotSupported() {
|
||||
try {
|
||||
gson.fromJson("-Infinity", BigDecimal.class);
|
||||
fail("Gson should not accept positive infinity for deserialization");
|
||||
} catch (JsonParseException expected) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user