/* * Copyright (C) 2008 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.smartandroid.sa.json; import java.lang.reflect.Type; import java.sql.Timestamp; import java.text.DateFormat; import java.util.Arrays; import java.util.Date; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import com.smartandroid.sa.json.DefaultTypeAdapters.DefaultDateTypeAdapter; import com.smartandroid.sa.json.internal.$Gson$Preconditions; /** * <p> * 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 use {@code new Gson()}. {@code GsonBuilder} * is best used by creating it, and then invoking its various configuration * methods, and finally calling create. * </p> * * <p> * The following is an example shows how to use the {@code GsonBuilder} to * construct a Gson instance: * * <pre> * Gson gson = new GsonBuilder() * .registerTypeAdapter(Id.class, new IdTypeAdapter()) * .enableComplexMapKeySerialization().serializeNulls() * .setDateFormat(DateFormat.LONG) * .setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE) * .setPrettyPrinting().setVersion(1.0).create(); * </pre> * * </p> * * <p> * NOTES: * <ul> * <li>the order of invocation of configuration methods does not matter.</li> * <li>The default serialization of {@link Date} and its subclasses in Gson does * not contain time-zone information. So, if you are using date/time instances, * use {@code GsonBuilder} and its {@code setDateFormat} methods.</li> * </ul> * </p> * * @author Inderjeet Singh * @author Joel Leitch */ public final class GsonBuilder { private static final MapAsArrayTypeAdapter COMPLEX_KEY_MAP_TYPE_ADAPTER = new MapAsArrayTypeAdapter(); private static final InnerClassExclusionStrategy innerClassExclusionStrategy = new InnerClassExclusionStrategy(); private static final ExposeAnnotationDeserializationExclusionStrategy exposeAnnotationDeserializationExclusionStrategy = new ExposeAnnotationDeserializationExclusionStrategy(); private static final ExposeAnnotationSerializationExclusionStrategy exposeAnnotationSerializationExclusionStrategy = new ExposeAnnotationSerializationExclusionStrategy(); private final Set<ExclusionStrategy> serializeExclusionStrategies = new HashSet<ExclusionStrategy>(); private final Set<ExclusionStrategy> deserializeExclusionStrategies = new HashSet<ExclusionStrategy>(); private double ignoreVersionsAfter; private ModifierBasedExclusionStrategy modifierBasedExclusionStrategy; private boolean serializeInnerClasses; private boolean excludeFieldsWithoutExposeAnnotation; private LongSerializationPolicy longSerializationPolicy; private FieldNamingStrategy2 fieldNamingPolicy; private final ParameterizedTypeHandlerMap<InstanceCreator<?>> instanceCreators; private final ParameterizedTypeHandlerMap<JsonSerializer<?>> serializers; private final ParameterizedTypeHandlerMap<JsonDeserializer<?>> deserializers; private boolean serializeNulls; private String datePattern; private int dateStyle; private int timeStyle; private boolean serializeSpecialFloatingPointValues; private boolean escapeHtmlChars; private boolean prettyPrinting; private boolean generateNonExecutableJson; /** * Creates a GsonBuilder instance that can be used to build Gson with * various configuration settings. GsonBuilder follows the builder pattern, * and it is typically used by first invoking various configuration methods * to set desired options, and finally calling {@link #create()}. */ public GsonBuilder() { // add default exclusion strategies deserializeExclusionStrategies .add(Gson.DEFAULT_ANON_LOCAL_CLASS_EXCLUSION_STRATEGY); deserializeExclusionStrategies .add(Gson.DEFAULT_SYNTHETIC_FIELD_EXCLUSION_STRATEGY); serializeExclusionStrategies .add(Gson.DEFAULT_ANON_LOCAL_CLASS_EXCLUSION_STRATEGY); serializeExclusionStrategies .add(Gson.DEFAULT_SYNTHETIC_FIELD_EXCLUSION_STRATEGY); // setup default values ignoreVersionsAfter = VersionConstants.IGNORE_VERSIONS; serializeInnerClasses = true; prettyPrinting = false; escapeHtmlChars = true; modifierBasedExclusionStrategy = Gson.DEFAULT_MODIFIER_BASED_EXCLUSION_STRATEGY; excludeFieldsWithoutExposeAnnotation = false; longSerializationPolicy = LongSerializationPolicy.DEFAULT; fieldNamingPolicy = Gson.DEFAULT_NAMING_POLICY; instanceCreators = new ParameterizedTypeHandlerMap<InstanceCreator<?>>(); serializers = new ParameterizedTypeHandlerMap<JsonSerializer<?>>(); deserializers = new ParameterizedTypeHandlerMap<JsonDeserializer<?>>(); serializeNulls = false; dateStyle = DateFormat.DEFAULT; timeStyle = DateFormat.DEFAULT; serializeSpecialFloatingPointValues = false; generateNonExecutableJson = false; } /** * Configures Gson to enable versioning support. * * @param ignoreVersionsAfter * any field or type marked with a version higher than this value * are ignored during serialization or deserialization. * @return a reference to this {@code GsonBuilder} object to fulfill the * "Builder" pattern */ public GsonBuilder setVersion(double ignoreVersionsAfter) { this.ignoreVersionsAfter = ignoreVersionsAfter; return this; } /** * Configures Gson to excludes all class fields that have the specified * modifiers. By default, Gson will exclude all fields marked transient or * static. This method will override that behavior. * * @param modifiers * the field modifiers. You must use the modifiers specified in * the {@link java.lang.reflect.Modifier} class. For example, * {@link java.lang.reflect.Modifier#TRANSIENT}, * {@link java.lang.reflect.Modifier#STATIC}. * @return a reference to this {@code GsonBuilder} object to fulfill the * "Builder" pattern */ public GsonBuilder excludeFieldsWithModifiers(int... modifiers) { modifierBasedExclusionStrategy = new ModifierBasedExclusionStrategy( modifiers); return this; } /** * Makes the output JSON non-executable in Javascript by prefixing the * generated JSON with some special text. This prevents attacks from * third-party sites through script sourcing. See <a * href="http://code.google.com/p/google-gson/issues/detail?id=42">Gson * Issue 42</a> for details. * * @return a reference to this {@code GsonBuilder} object to fulfill the * "Builder" pattern * @since 1.3 */ public GsonBuilder generateNonExecutableJson() { this.generateNonExecutableJson = true; return this; } /** * Configures Gson to exclude all fields from consideration for * serialization or deserialization that do not have the * {@link com.google.gson.annotations.Expose} annotation. * * @return a reference to this {@code GsonBuilder} object to fulfill the * "Builder" pattern */ public GsonBuilder excludeFieldsWithoutExposeAnnotation() { excludeFieldsWithoutExposeAnnotation = true; return this; } /** * Configure Gson to serialize null fields. By default, Gson omits all * fields that are null during serialization. * * @return a reference to this {@code GsonBuilder} object to fulfill the * "Builder" pattern * @since 1.2 */ public GsonBuilder serializeNulls() { this.serializeNulls = true; return this; } /** * Enabling this feature will only change the serialized form if the map key * is a complex type (i.e. non-primitive) in its <strong>serialized</strong> * JSON form. The default implementation of map serialization uses * {@code toString()} on the key; however, when this is called then one of * the following cases apply: * * <h3>Maps as JSON objects</h3> For this case, assume that a type adapter * is registered to serialize and deserialize some {@code Point} class, * which contains an x and y coordinate, to/from the JSON Primitive string * value {@code "(x,y)"}. The Java map would then be serialized as a * {@link JsonObject}. * * <p> * Below is an example: * * <pre> * { * @code * Gson gson = new GsonBuilder() * .register(Point.class, new MyPointTypeAdapter()) * .enableComplexMapKeySerialization().create(); * * Map<Point, String> original = new LinkedHashMap<Point, String>(); * original.put(new Point(5, 6), "a"); * original.put(new Point(8, 8), "b"); * System.out.println(gson.toJson(original, type)); * } * </pre> * * The above code prints this JSON object: * * <pre> * {@code * { * "(5,6)": "a", * "(8,8)": "b" * } * } * </pre> * * <h3>Maps as JSON arrays</h3> * For this case, assume that a type adapter was NOT registered for some * {@code Point} class, but rather the default Gson serialization is * applied. In this case, some {@code new Point(2,3)} would serialize as * {@code {"x":2,"y":5}}. * * <p> * Given the assumption above, a {@code Map<Point, String>} will be * serialize as an array of arrays (can be viewed as an entry set of pairs). * * <p> * Below is an example of serializing complex types as JSON arrays: * * <pre> * { * @code * Gson gson = new GsonBuilder().enableComplexMapKeySerialization().create(); * * Map<Point, String> original = new LinkedHashMap<Point, String>(); * original.put(new Point(5, 6), "a"); * original.put(new Point(8, 8), "b"); * System.out.println(gson.toJson(original, type)); * } * * The JSON output would look as follows: * * <pre> * {@code * [ * [ * { * "x": 5, * "y": 6 * }, * "a" * ], * [ * { * "x": 8, * "y": 8 * }, * "b" * ] * ] * } * </pre> * * @return a reference to this {@code GsonBuilder} object to fulfill the * "Builder" pattern * @since 1.7 */ public GsonBuilder enableComplexMapKeySerialization() { registerTypeHierarchyAdapter(Map.class, COMPLEX_KEY_MAP_TYPE_ADAPTER); 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 */ public GsonBuilder disableInnerClassSerialization() { 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. * * @param namingConvention * the JSON field naming convention to use for serialization and * deserialization. * @return a reference to this {@code GsonBuilder} object to fulfill the * "Builder" pattern */ public GsonBuilder setFieldNamingPolicy(FieldNamingPolicy namingConvention) { return setFieldNamingStrategy(namingConvention.getFieldNamingPolicy()); } /** * Configures Gson to apply a specific naming policy strategy to an object's * field during serialization and deserialization. * * @param fieldNamingStrategy * the actual naming strategy to apply to the fields * @return a reference to this {@code GsonBuilder} object to fulfill the * "Builder" pattern * @since 1.3 */ public GsonBuilder setFieldNamingStrategy( FieldNamingStrategy fieldNamingStrategy) { return setFieldNamingStrategy(new FieldNamingStrategy2Adapter( fieldNamingStrategy)); } /** * Configures Gson to apply a specific naming policy strategy to an object's * field during serialization and deserialization. * * @param fieldNamingStrategy * the actual naming strategy to apply to the fields * @return a reference to this {@code GsonBuilder} object to fulfill the * "Builder" pattern */ GsonBuilder setFieldNamingStrategy(FieldNamingStrategy2 fieldNamingStrategy) { this.fieldNamingPolicy = new SerializedNameAnnotationInterceptingNamingPolicy( fieldNamingStrategy); return this; } /** * Configures Gson to apply a set of exclusion strategies during both * serialization and deserialization. Each of the {@code strategies} will be * applied as a disjunction rule. This means that if one of the * {@code strategies} suggests that a field (or class) should be skipped * then that field (or object) is skipped during * serializaiton/deserialization. * * @param strategies * the set of strategy object to apply during object * (de)serialization. * @return a reference to this {@code GsonBuilder} object to fulfill the * "Builder" pattern * @since 1.4 */ public GsonBuilder setExclusionStrategies(ExclusionStrategy... strategies) { List<ExclusionStrategy> strategyList = Arrays.asList(strategies); serializeExclusionStrategies.addAll(strategyList); deserializeExclusionStrategies.addAll(strategyList); return this; } /** * Configures Gson to apply the passed in exclusion strategy during * serialization. If this method is invoked numerous times with different * exclusion strategy objects then the exclusion strategies that were added * will be applied as a disjunction rule. This means that if one of the * added exclusion strategies suggests that a field (or class) should be * skipped then that field (or object) is skipped during its serialization. * * @param strategy * an exclusion strategy to apply during serialization. * @return a reference to this {@code GsonBuilder} object to fulfill the * "Builder" pattern * @since 1.7 */ public GsonBuilder addSerializationExclusionStrategy( ExclusionStrategy strategy) { serializeExclusionStrategies.add(strategy); return this; } /** * Configures Gson to apply the passed in exclusion strategy during * deserialization. If this method is invoked numerous times with different * exclusion strategy objects then the exclusion strategies that were added * will be applied as a disjunction rule. This means that if one of the * added exclusion strategies suggests that a field (or class) should be * skipped then that field (or object) is skipped during its * deserialization. * * @param strategy * an exclusion strategy to apply during deserialization. * @return a reference to this {@code GsonBuilder} object to fulfill the * "Builder" pattern * @since 1.7 */ public GsonBuilder addDeserializationExclusionStrategy( ExclusionStrategy strategy) { deserializeExclusionStrategies.add(strategy); return this; } /** * Configures Gson to output Json that fits in a page for pretty printing. * This option only affects Json serialization. * * @return a reference to this {@code GsonBuilder} object to fulfill the * "Builder" pattern */ public GsonBuilder setPrettyPrinting() { 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 */ public GsonBuilder disableHtmlEscaping() { this.escapeHtmlChars = false; return this; } /** * Configures Gson to serialize {@code Date} objects according to the * pattern provided. You can call this method or {@link #setDateFormat(int)} * multiple times, but only the last invocation will be used to decide the * serialization format. * * <p> * The date format will be used to serialize and deserialize * {@link java.util.Date}, {@link java.sql.Timestamp} and * {@link java.sql.Date}. * * <p> * Note that this pattern must abide by the convention provided by * {@code SimpleDateFormat} class. See the documentation in * {@link java.text.SimpleDateFormat} for more information on valid date and * time patterns. * </p> * * @param pattern * the pattern that dates will be serialized/deserialized to/from * @return a reference to this {@code GsonBuilder} object to fulfill the * "Builder" pattern * @since 1.2 */ public GsonBuilder setDateFormat(String pattern) { // TODO(Joel): Make this fail fast if it is an invalid date format this.datePattern = pattern; return this; } /** * Configures Gson to to serialize {@code Date} objects according to the * style value provided. You can call this method or * {@link #setDateFormat(String)} multiple times, but only the last * invocation will be used to decide the serialization format. * * <p> * Note that this style value should be one of the predefined constants in * the {@code DateFormat} class. See the documentation in * {@link java.text.DateFormat} for more information on the valid style * constants. * </p> * * @param style * the predefined date style that date objects will be * serialized/deserialized to/from * @return a reference to this {@code GsonBuilder} object to fulfill the * "Builder" pattern * @since 1.2 */ public GsonBuilder setDateFormat(int style) { this.dateStyle = style; this.datePattern = null; return this; } /** * Configures Gson to to serialize {@code Date} objects according to the * style value provided. You can call this method or * {@link #setDateFormat(String)} multiple times, but only the last * invocation will be used to decide the serialization format. * * <p> * Note that this style value should be one of the predefined constants in * the {@code DateFormat} class. See the documentation in * {@link java.text.DateFormat} for more information on the valid style * constants. * </p> * * @param dateStyle * the predefined date style that date objects will be * serialized/deserialized to/from * @param timeStyle * the predefined style for the time portion of the date objects * @return a reference to this {@code GsonBuilder} object to fulfill the * "Builder" pattern * @since 1.2 */ public GsonBuilder setDateFormat(int dateStyle, int timeStyle) { this.dateStyle = dateStyle; this.timeStyle = timeStyle; 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 {@link JsonDeserializer}. It is best used * when a single object {@code typeAdapter} implements all the required * interfaces for custom serialization with Gson. If an instance creator, * serializer or deserializer was previously registered for the specified * {@code type}, it is overwritten. * * @param type * the type definition for the type adapter being registered * @param typeAdapter * This object must implement at least one of the * {@link InstanceCreator}, {@link JsonSerializer}, and a * {@link JsonDeserializer} interfaces. * @return a reference to this {@code GsonBuilder} object to fulfill the * "Builder" pattern */ public GsonBuilder registerTypeAdapter(Type type, Object typeAdapter) { return registerTypeAdapter(type, typeAdapter, false); } private GsonBuilder registerTypeAdapter(Type type, Object typeAdapter, boolean isSystem) { $Gson$Preconditions .checkArgument(typeAdapter instanceof JsonSerializer<?> || typeAdapter instanceof JsonDeserializer<?> || typeAdapter instanceof InstanceCreator<?>); if (typeAdapter instanceof InstanceCreator<?>) { registerInstanceCreator(type, (InstanceCreator<?>) typeAdapter, isSystem); } if (typeAdapter instanceof JsonSerializer<?>) { registerSerializer(type, (JsonSerializer<?>) typeAdapter, isSystem); } if (typeAdapter instanceof JsonDeserializer<?>) { registerDeserializer(type, (JsonDeserializer<?>) typeAdapter, isSystem); } return this; } /** * Configures Gson to use a custom {@link InstanceCreator} for the specified * type. If an instance creator was previously registered for the specified * class, it is overwritten. Since this method takes a type instead of a * Class object, it can be used to register a specific handler for a generic * type corresponding to a raw type. * * @param <T> * the type for which instance creator is being registered * @param typeOfT * The Type definition for T * @param instanceCreator * the instance creator for T * @return a reference to this {@code GsonBuilder} object to fulfill the * "Builder" pattern */ private <T> GsonBuilder registerInstanceCreator(Type typeOfT, InstanceCreator<? extends T> instanceCreator, boolean isSystem) { instanceCreators.register(typeOfT, instanceCreator, isSystem); return this; } /** * Configures Gson to use a custom JSON serializer for the specified type. * You should use this method if you want to register different serializers * for different generic types corresponding to a raw type. * * @param <T> * the type for which the serializer is being registered * @param typeOfT * The type definition for T * @param serializer * the custom serializer * @return a reference to this {@code GsonBuilder} object to fulfill the * "Builder" pattern */ private <T> GsonBuilder registerSerializer(Type typeOfT, JsonSerializer<T> serializer, boolean isSystem) { serializers.register(typeOfT, serializer, isSystem); return this; } /** * Configures Gson to use a custom JSON deserializer for the specified type. * You should use this method if you want to register different * deserializers for different generic types corresponding to a raw type. * * @param <T> * the type for which the deserializer is being registered * @param typeOfT * The type definition for T * @param deserializer * the custom deserializer * @return a reference to this {@code GsonBuilder} object to fulfill the * "Builder" pattern */ private <T> GsonBuilder registerDeserializer(Type typeOfT, JsonDeserializer<T> deserializer, boolean isSystem) { deserializers .register(typeOfT, new JsonDeserializerExceptionWrapper<T>( deserializer), isSystem); return this; } /** * Configures Gson for custom serialization or deserialization for an * inheritance type hierarchy. This method combines the registration of an * {@link InstanceCreator}, {@link JsonSerializer}, and a * {@link JsonDeserializer}. It is best used when a single object * {@code typeAdapter} implements all the required interfaces for custom * serialization with Gson. If an instance creator, serializer or * deserializer was previously registered for the specified type hierarchy, * it is overwritten. If an instance creator, serializer or deserializer is * registered for a specific type in the type hierarchy, it will be invoked * instead of the one registered for the type hierarchy. * * @param baseType * the class definition for the type adapter being registered for * the base class or interface * @param typeAdapter * This object must implement at least one of the * {@link InstanceCreator}, {@link JsonSerializer}, and a * {@link JsonDeserializer} interfaces. * @return a reference to this {@code GsonBuilder} object to fulfill the * "Builder" pattern * @since 1.7 */ public GsonBuilder registerTypeHierarchyAdapter(Class<?> baseType, Object typeAdapter) { return registerTypeHierarchyAdapter(baseType, typeAdapter, false); } private GsonBuilder registerTypeHierarchyAdapter(Class<?> baseType, Object typeAdapter, boolean isSystem) { $Gson$Preconditions .checkArgument(typeAdapter instanceof JsonSerializer<?> || typeAdapter instanceof JsonDeserializer<?> || typeAdapter instanceof InstanceCreator<?>); if (typeAdapter instanceof InstanceCreator<?>) { registerInstanceCreatorForTypeHierarchy(baseType, (InstanceCreator<?>) typeAdapter, isSystem); } if (typeAdapter instanceof JsonSerializer<?>) { registerSerializerForTypeHierarchy(baseType, (JsonSerializer<?>) typeAdapter, isSystem); } if (typeAdapter instanceof JsonDeserializer<?>) { registerDeserializerForTypeHierarchy(baseType, (JsonDeserializer<?>) typeAdapter, isSystem); } return this; } private <T> GsonBuilder registerInstanceCreatorForTypeHierarchy( Class<?> classOfT, InstanceCreator<? extends T> instanceCreator, boolean isSystem) { instanceCreators.registerForTypeHierarchy(classOfT, instanceCreator, isSystem); return this; } private <T> GsonBuilder registerSerializerForTypeHierarchy( Class<?> classOfT, JsonSerializer<T> serializer, boolean isSystem) { serializers.registerForTypeHierarchy(classOfT, serializer, isSystem); return this; } private <T> GsonBuilder registerDeserializerForTypeHierarchy( Class<?> classOfT, JsonDeserializer<T> deserializer, boolean isSystem) { deserializers .registerForTypeHierarchy(classOfT, new JsonDeserializerExceptionWrapper<T>(deserializer), isSystem); 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. * * @return an instance of Gson configured with the options currently set in * this builder */ public Gson create() { List<ExclusionStrategy> deserializationStrategies = new LinkedList<ExclusionStrategy>( deserializeExclusionStrategies); List<ExclusionStrategy> serializationStrategies = new LinkedList<ExclusionStrategy>( serializeExclusionStrategies); deserializationStrategies.add(modifierBasedExclusionStrategy); serializationStrategies.add(modifierBasedExclusionStrategy); if (!serializeInnerClasses) { deserializationStrategies.add(innerClassExclusionStrategy); serializationStrategies.add(innerClassExclusionStrategy); } if (ignoreVersionsAfter != VersionConstants.IGNORE_VERSIONS) { VersionExclusionStrategy versionExclusionStrategy = new VersionExclusionStrategy( ignoreVersionsAfter); deserializationStrategies.add(versionExclusionStrategy); serializationStrategies.add(versionExclusionStrategy); } if (excludeFieldsWithoutExposeAnnotation) { deserializationStrategies .add(exposeAnnotationDeserializationExclusionStrategy); serializationStrategies .add(exposeAnnotationSerializationExclusionStrategy); } ParameterizedTypeHandlerMap<JsonSerializer<?>> customSerializers = DefaultTypeAdapters.DEFAULT_HIERARCHY_SERIALIZERS .copyOf(); customSerializers.register(serializers.copyOf()); ParameterizedTypeHandlerMap<JsonDeserializer<?>> customDeserializers = DefaultTypeAdapters.DEFAULT_HIERARCHY_DESERIALIZERS .copyOf(); customDeserializers.register(deserializers.copyOf()); addTypeAdaptersForDate(datePattern, dateStyle, timeStyle, customSerializers, customDeserializers); customDeserializers.registerIfAbsent(DefaultTypeAdapters .getDefaultDeserializers()); ParameterizedTypeHandlerMap<InstanceCreator<?>> customInstanceCreators = instanceCreators .copyOf(); customInstanceCreators.registerIfAbsent(DefaultTypeAdapters .getDefaultInstanceCreators()); customSerializers.makeUnmodifiable(); customDeserializers.makeUnmodifiable(); instanceCreators.makeUnmodifiable(); MappedObjectConstructor objConstructor = new MappedObjectConstructor( customInstanceCreators); return new Gson(new DisjunctionExclusionStrategy( deserializationStrategies), new DisjunctionExclusionStrategy( serializationStrategies), fieldNamingPolicy, objConstructor, serializeNulls, customSerializers, customDeserializers, generateNonExecutableJson, escapeHtmlChars, prettyPrinting, serializeSpecialFloatingPointValues, longSerializationPolicy); } private static void addTypeAdaptersForDate(String datePattern, int dateStyle, int timeStyle, ParameterizedTypeHandlerMap<JsonSerializer<?>> serializers, ParameterizedTypeHandlerMap<JsonDeserializer<?>> deserializers) { DefaultDateTypeAdapter dateTypeAdapter = null; if (datePattern != null && !"".equals(datePattern.trim())) { dateTypeAdapter = new DefaultDateTypeAdapter(datePattern); } else if (dateStyle != DateFormat.DEFAULT && timeStyle != DateFormat.DEFAULT) { dateTypeAdapter = new DefaultDateTypeAdapter(dateStyle, timeStyle); } if (dateTypeAdapter != null) { registerIfAbsent(Date.class, serializers, dateTypeAdapter); registerIfAbsent(Date.class, deserializers, dateTypeAdapter); registerIfAbsent(Timestamp.class, serializers, dateTypeAdapter); registerIfAbsent(Timestamp.class, deserializers, dateTypeAdapter); registerIfAbsent(java.sql.Date.class, serializers, dateTypeAdapter); registerIfAbsent(java.sql.Date.class, deserializers, dateTypeAdapter); } } private static <T> void registerIfAbsent(Class<?> type, ParameterizedTypeHandlerMap<T> adapters, T adapter) { if (!adapters.hasSpecificHandlerFor(type)) { adapters.register(type, adapter, false); } } }