From e2cfc0cc19fb917ad5d9923706fe33846628ca36 Mon Sep 17 00:00:00 2001 From: Joel Leitch Date: Sun, 11 Jan 2009 06:11:29 +0000 Subject: [PATCH] Update GsonBuilder API to accept an enum for the serialize Long as string. This will be useful if/when we implement support to serialize a long type as an array of integers. --- .../com/google/gson/DefaultTypeAdapters.java | 20 ++--- .../java/com/google/gson/GsonBuilder.java | 78 +++++++++---------- .../google/gson/LongSerializationPolicy.java | 74 ++++++++++++++++++ .../gson/LongSerializationPolicyTest.java | 48 ++++++++++++ .../google/gson/functional/PrimitiveTest.java | 5 +- 5 files changed, 171 insertions(+), 54 deletions(-) create mode 100644 gson/src/main/java/com/google/gson/LongSerializationPolicy.java create mode 100644 gson/src/test/java/com/google/gson/LongSerializationPolicyTest.java diff --git a/gson/src/main/java/com/google/gson/DefaultTypeAdapters.java b/gson/src/main/java/com/google/gson/DefaultTypeAdapters.java index 9810b2b6..af4514ea 100644 --- a/gson/src/main/java/com/google/gson/DefaultTypeAdapters.java +++ b/gson/src/main/java/com/google/gson/DefaultTypeAdapters.java @@ -186,11 +186,11 @@ final class DefaultTypeAdapters { } static ParameterizedTypeHandlerMap> getDefaultSerializers() { - return getDefaultSerializers(false, false); + return getDefaultSerializers(false, LongSerializationPolicy.DEFAULT); } static ParameterizedTypeHandlerMap> getDefaultSerializers( - boolean serializeSpecialFloatingPointValues, boolean serializeLongsAsString) { + boolean serializeSpecialFloatingPointValues, LongSerializationPolicy longSerializationPolicy) { ParameterizedTypeHandlerMap> serializers = new ParameterizedTypeHandlerMap>(); @@ -208,7 +208,7 @@ final class DefaultTypeAdapters { // Long primitive DefaultTypeAdapters.LongSerializer longSerializer = - new DefaultTypeAdapters.LongSerializer(serializeLongsAsString); + new DefaultTypeAdapters.LongSerializer(longSerializationPolicy); serializers.registerIfAbsent(Long.class, longSerializer); serializers.registerIfAbsent(long.class, longSerializer); @@ -564,20 +564,14 @@ final class DefaultTypeAdapters { } private static class LongSerializer implements JsonSerializer { - private final boolean serializeAsString; + private final LongSerializationPolicy longSerializationPolicy; - private LongSerializer(boolean serializeAsString) { - this.serializeAsString = serializeAsString; + private LongSerializer(LongSerializationPolicy longSerializationPolicy) { + this.longSerializationPolicy = longSerializationPolicy; } 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); - } + return longSerializationPolicy.serialize(src); } @Override diff --git a/gson/src/main/java/com/google/gson/GsonBuilder.java b/gson/src/main/java/com/google/gson/GsonBuilder.java index 8e0d3c95..a075c4f7 100644 --- a/gson/src/main/java/com/google/gson/GsonBuilder.java +++ b/gson/src/main/java/com/google/gson/GsonBuilder.java @@ -16,14 +16,14 @@ package com.google.gson; -import com.google.gson.DefaultTypeAdapters.DefaultDateTypeAdapter; - import java.lang.reflect.Type; import java.text.DateFormat; import java.util.Date; import java.util.LinkedList; import java.util.List; +import com.google.gson.DefaultTypeAdapters.DefaultDateTypeAdapter; + /** *

Use this builder to construct a {@link Gson} instance when you need to set configuration * options other than the default. For {@link Gson} with default configuration, it is simpler to @@ -52,12 +52,12 @@ import java.util.List; public final class GsonBuilder { private double ignoreVersionsAfter; - private boolean serializeLongAsString; private ModifierBasedExclusionStrategy modifierBasedExclusionStrategy; private boolean serializeInnerClasses; private final AnonymousAndLocalClassExclusionStrategy anonAndLocalClassExclusionStrategy; private final InnerClassExclusionStrategy innerClassExclusionStrategy; private boolean excludeFieldsWithoutExposeAnnotation; + private LongSerializationPolicy longSerializationPolicy; private FieldNamingStrategy fieldNamingPolicy; private final ParameterizedTypeHandlerMap> instanceCreators; private final ParameterizedTypeHandlerMap> serializers; @@ -79,7 +79,6 @@ public final class GsonBuilder { public GsonBuilder() { // setup default values ignoreVersionsAfter = VersionConstants.IGNORE_VERSIONS; - serializeLongAsString = false; serializeInnerClasses = true; prettyPrinting = false; escapeHtmlChars = true; @@ -87,6 +86,7 @@ public final class GsonBuilder { innerClassExclusionStrategy = new InnerClassExclusionStrategy(); modifierBasedExclusionStrategy = Gson.DEFAULT_MODIFIER_BASED_EXCLUSION_STRATEGY; excludeFieldsWithoutExposeAnnotation = false; + longSerializationPolicy = LongSerializationPolicy.DEFAULT; fieldNamingPolicy = Gson.DEFAULT_NAMING_POLICY; instanceCreators = new ParameterizedTypeHandlerMap>(); serializers = new ParameterizedTypeHandlerMap>(); @@ -148,23 +148,10 @@ public final class GsonBuilder { this.serializeNulls = true; return this; } - - /** - * 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 - * @since 1.3 - */ - public GsonBuilder serializeLongFieldsAsString(boolean value) { - serializeLongAsString = value; - return this; - } /** * Configures Gson to exclude inner classes during serialization. - * + * * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern * @since 1.3 */ @@ -172,7 +159,20 @@ public final class GsonBuilder { serializeInnerClasses = false; return this; } - + + /** + * Configures Gson to apply a specific serialization policy for {@code Long} and {@code long} + * objects. + * + * @param serializationPolicy the particular policy to use for serializing longs. + * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern + * @since 1.3 + */ + public GsonBuilder setLongSerializationPolicy(LongSerializationPolicy serializationPolicy) { + this.longSerializationPolicy = serializationPolicy; + return this; + } + /** * Configures Gson to apply a specific naming policy to an object's field during serialization * and deserialization. @@ -207,11 +207,11 @@ public final class GsonBuilder { prettyPrinting = true; return this; } - + /** * By default, Gson escapes HTML characters such as < > etc. Use this option to configure * Gson to pass-through HTML characters as is. - * + * * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern * @since 1.3 */ @@ -280,7 +280,7 @@ public final class GsonBuilder { this.datePattern = null; return this; } - + /** * Configures Gson for custom serialization or deserialization. This method combines the * registration of an {@link InstanceCreator}, {@link JsonSerializer}, and a @@ -358,21 +358,21 @@ public final class GsonBuilder { /** * Section 2.4 of JSON specification disallows - * special double values (NaN, Infinity, -Infinity). However, - * Javascript + * special double values (NaN, Infinity, -Infinity). However, + * Javascript * specification (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 + * 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. - * - *

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 + * though JSON specification disallows them. + * + *

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. - * + * values. + * * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern * @since 1.3 */ @@ -409,18 +409,18 @@ public final class GsonBuilder { customDeserializers); customSerializers.registerIfAbsent(DefaultTypeAdapters.getDefaultSerializers( - serializeSpecialFloatingPointValues, serializeLongAsString)); - + serializeSpecialFloatingPointValues, longSerializationPolicy)); + customDeserializers.registerIfAbsent(DefaultTypeAdapters.getDefaultDeserializers()); - + ParameterizedTypeHandlerMap> customInstanceCreators = instanceCreators.copyOf(); customInstanceCreators.registerIfAbsent(DefaultTypeAdapters.getDefaultInstanceCreators()); MappedObjectConstructor objConstructor = Gson.createObjectConstructor(customInstanceCreators); - JsonFormatter formatter = prettyPrinting ? - new JsonPrintFormatter(escapeHtmlChars) : new JsonCompactFormatter(escapeHtmlChars); - Gson gson = new Gson(exclusionStrategy, fieldNamingPolicy, objConstructor, + JsonFormatter formatter = prettyPrinting ? + new JsonPrintFormatter(escapeHtmlChars) : new JsonCompactFormatter(escapeHtmlChars); + Gson gson = new Gson(exclusionStrategy, fieldNamingPolicy, objConstructor, formatter, serializeNulls, customSerializers, customDeserializers); return gson; } diff --git a/gson/src/main/java/com/google/gson/LongSerializationPolicy.java b/gson/src/main/java/com/google/gson/LongSerializationPolicy.java new file mode 100644 index 00000000..aff45263 --- /dev/null +++ b/gson/src/main/java/com/google/gson/LongSerializationPolicy.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2009 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.gson; + +/** + * Defines the expected format for a {@code long} or {@code Long} type when its serialized. + * + * @since 1.3 + * + * @author Inderjeet Singh + * @author Joel Leitch + */ +public enum LongSerializationPolicy { + /** + * This is the "default" serialization policy that will output a {@code long} object as a JSON + * number. For example, assume an object has a long field named "f" then the serialized output + * would be: + * {@code {"f":123}}. + */ + DEFAULT(new DefaultStrategy()), + + /** + * Serializes a long value as a quoted string. For example, assume an object has a long field + * named "f" then the serialized output would be: + * {@code {"f":"123"}}. + */ + STRING(new StringStrategy()); + + private final Strategy strategy; + + private LongSerializationPolicy(Strategy strategy) { + this.strategy = strategy; + } + + /** + * Serialize this {@code value} using this serialization policy. + * + * @param value the long value to be serialized into a {@link JsonElement} + * @return the serialized version of {@code value} + */ + public JsonElement serialize(Long value) { + return strategy.serialize(value); + } + + private interface Strategy { + JsonElement serialize(Long value); + } + + private static class DefaultStrategy implements Strategy { + public JsonElement serialize(Long value) { + return new JsonPrimitive(value); + } + } + + private static class StringStrategy implements Strategy { + public JsonElement serialize(Long value) { + return new JsonPrimitive(String.valueOf(value)); + } + } +} diff --git a/gson/src/test/java/com/google/gson/LongSerializationPolicyTest.java b/gson/src/test/java/com/google/gson/LongSerializationPolicyTest.java new file mode 100644 index 00000000..111a1719 --- /dev/null +++ b/gson/src/test/java/com/google/gson/LongSerializationPolicyTest.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2009 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.gson; + +import junit.framework.TestCase; + +/** + * Unit test for the {@link LongSerializationPolicy} class. + * + * @author Inderjeet Singh + * @author Joel Leitch + */ +public class LongSerializationPolicyTest extends TestCase { + + public void testDefaultLongSerialization() throws Exception { + JsonElement element = LongSerializationPolicy.DEFAULT.serialize(1556L); + assertTrue(element.isJsonPrimitive()); + + JsonPrimitive jsonPrimitive = element.getAsJsonPrimitive(); + assertFalse(jsonPrimitive.isString()); + assertTrue(jsonPrimitive.isNumber()); + assertEquals(1556L, element.getAsLong()); + } + + public void testStringLongSerialization() throws Exception { + JsonElement element = LongSerializationPolicy.STRING.serialize(1556L); + assertTrue(element.isJsonPrimitive()); + + JsonPrimitive jsonPrimitive = element.getAsJsonPrimitive(); + assertFalse(jsonPrimitive.isNumber()); + assertTrue(jsonPrimitive.isString()); + assertEquals("1556", element.getAsString()); + } +} diff --git a/gson/src/test/java/com/google/gson/functional/PrimitiveTest.java b/gson/src/test/java/com/google/gson/functional/PrimitiveTest.java index 6d24d092..b62fdb91 100644 --- a/gson/src/test/java/com/google/gson/functional/PrimitiveTest.java +++ b/gson/src/test/java/com/google/gson/functional/PrimitiveTest.java @@ -24,6 +24,7 @@ import junit.framework.TestCase; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonParseException; +import com.google.gson.LongSerializationPolicy; import com.google.gson.common.TestTypes.CrazyLongTypeAdapter; /** @@ -530,7 +531,7 @@ public class PrimitiveTest extends TestCase { } public void testLongAsStringSerialization() throws Exception { - gson = new GsonBuilder().serializeLongFieldsAsString(true).create(); + gson = new GsonBuilder().setLongSerializationPolicy(LongSerializationPolicy.STRING).create(); String result = gson.toJson(15L); assertEquals("\"15\"", result); @@ -543,7 +544,7 @@ public class PrimitiveTest extends TestCase { long value = gson.fromJson("\"15\"", long.class); assertEquals(15, value); - gson = new GsonBuilder().serializeLongFieldsAsString(true).create(); + gson = new GsonBuilder().setLongSerializationPolicy(LongSerializationPolicy.STRING).create(); value = gson.fromJson("\"25\"", long.class); assertEquals(25, value); }