/* * 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.google.gson; import static com.google.gson.internal.DefaultConfig.*; import com.google.gson.annotations.Since; import com.google.gson.annotations.Until; import com.google.gson.internal.$Gson$Preconditions; import com.google.gson.internal.Excluder; import com.google.gson.internal.bind.DefaultDateTypeAdapter; import com.google.gson.internal.bind.TreeTypeAdapter; import com.google.gson.internal.bind.TypeAdapters; import com.google.gson.internal.sql.SqlTypesSupport; import com.google.gson.reflect.TypeToken; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; import java.lang.reflect.Type; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; /** * 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. * *

The following example shows how to use the {@code GsonBuilder} to construct a Gson instance: * *

 * Gson gson = new GsonBuilder()
 *     .registerTypeAdapter(Id.class, new IdTypeAdapter())
 *     .enableComplexMapKeySerialization()
 *     .serializeNulls()
 *     .setDateFormat(DateFormat.LONG, DateFormat.LONG)
 *     .setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE)
 *     .setPrettyPrinting()
 *     .setVersion(1.0)
 *     .create();
 * 
* *

Notes: * *

* * @author Inderjeet Singh * @author Joel Leitch * @author Jesse Wilson */ public final class GsonBuilder { private Excluder excluder = Excluder.DEFAULT; private LongSerializationPolicy longSerializationPolicy = LongSerializationPolicy.DEFAULT; private FieldNamingStrategy fieldNamingPolicy = FieldNamingPolicy.IDENTITY; private final Map> instanceCreators = new HashMap<>(); private final List factories = new ArrayList<>(); /** tree-style hierarchy factories. These come after factories for backwards compatibility. */ private final List hierarchyFactories = new ArrayList<>(); private boolean serializeNulls = DEFAULT_SERIALIZE_NULLS; private String datePattern = DEFAULT_DATE_PATTERN; private int dateStyle = DateFormat.DEFAULT; private int timeStyle = DateFormat.DEFAULT; private boolean complexMapKeySerialization = DEFAULT_COMPLEX_MAP_KEYS; private boolean duplicateMapKeyDeserialization = DEFAULT_DUPLICATE_MAP_KEYS; private boolean serializeSpecialFloatingPointValues = DEFAULT_SPECIALIZE_FLOAT_VALUES; private boolean escapeHtmlChars = DEFAULT_ESCAPE_HTML; private FormattingStyle formattingStyle = DEFAULT_FORMATTING_STYLE; private boolean generateNonExecutableJson = DEFAULT_JSON_NON_EXECUTABLE; private Strictness strictness = DEFAULT_STRICTNESS; private boolean omitQuotes = DEFAULT_OMIT_QUOTES; private boolean useJdkUnsafe = DEFAULT_USE_JDK_UNSAFE; private ToNumberStrategy objectToNumberStrategy = DEFAULT_OBJECT_TO_NUMBER_STRATEGY; private ToNumberStrategy numberToNumberStrategy = DEFAULT_NUMBER_TO_NUMBER_STRATEGY; private final ArrayDeque reflectionFilters = new ArrayDeque<>(); /** * 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() {} /** * Constructs a GsonBuilder instance from a Gson instance. The newly constructed GsonBuilder has * the same configuration as the previously built Gson instance. * * @param gson the gson instance whose configuration should be applied to a new GsonBuilder. */ GsonBuilder(Gson gson) { this.excluder = gson.excluder; this.fieldNamingPolicy = gson.fieldNamingStrategy; this.instanceCreators.putAll(gson.instanceCreators); this.serializeNulls = gson.serializeNulls; this.complexMapKeySerialization = gson.complexMapKeySerialization; this.duplicateMapKeyDeserialization = gson.duplicateMapKeyDeserialization; this.generateNonExecutableJson = gson.generateNonExecutableJson; this.escapeHtmlChars = gson.htmlSafe; this.formattingStyle = gson.formattingStyle; this.strictness = gson.strictness; this.omitQuotes = gson.omitQuotes; this.serializeSpecialFloatingPointValues = gson.serializeSpecialFloatingPointValues; this.longSerializationPolicy = gson.longSerializationPolicy; this.datePattern = gson.datePattern; this.dateStyle = gson.dateStyle; this.timeStyle = gson.timeStyle; this.factories.addAll(gson.builderFactories); this.hierarchyFactories.addAll(gson.builderHierarchyFactories); this.useJdkUnsafe = gson.useJdkUnsafe; this.objectToNumberStrategy = gson.objectToNumberStrategy; this.numberToNumberStrategy = gson.numberToNumberStrategy; this.reflectionFilters.addAll(gson.reflectionFilters); } /** * Configures Gson to enable versioning support. Versioning support works based on the annotation * types {@link Since} and {@link Until}. It allows including or excluding fields and classes * based on the specified version. See the documentation of these annotation types for more * information. * *

By default versioning support is disabled and usage of {@code @Since} and {@code @Until} has * no effect. * * @param version the version number to use. * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern * @throws IllegalArgumentException if the version number is NaN or negative * @see Since * @see Until */ public GsonBuilder setVersion(double version) { if (Double.isNaN(version) || version < 0.0) { throw new IllegalArgumentException("Invalid version: " + version); } excluder = excluder.withVersion(version); return this; } /** * Configures Gson to excludes all class fields that have the specified modifiers. By default, * Gson will exclude all fields marked {@code transient} or {@code static}. This method will * override that behavior. * *

This is a convenience method which behaves as if an {@link ExclusionStrategy} which excludes * these fields was {@linkplain #setExclusionStrategies(ExclusionStrategy...) registered with this * builder}. * * @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) { Objects.requireNonNull(modifiers); excluder = excluder.withModifiers(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 Gson Issue 42 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 and deserialization * that do not have the {@link com.google.gson.annotations.Expose} annotation. * *

This is a convenience method which behaves as if an {@link ExclusionStrategy} which excludes * these fields was {@linkplain #setExclusionStrategies(ExclusionStrategy...) registered with this * builder}. * * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern */ public GsonBuilder excludeFieldsWithoutExposeAnnotation() { excluder = excluder.excludeFieldsWithoutExposeAnnotation(); return this; } /** * Configures 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; } /** * Configures Gson to serialize {@code Map} objects with complex keys as JSON arrays. Enabling * this feature will only change the serialized form if the map key is a complex type (i.e. * non-primitive) in its serialized 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: * *

Maps as JSON objects * *

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}. * *

Below is an example: * *

{@code
   * Gson gson = new GsonBuilder()
   *     .register(Point.class, new MyPointTypeAdapter())
   *     .enableComplexMapKeySerialization()
   *     .create();
   *
   * Map original = new LinkedHashMap<>();
   * original.put(new Point(5, 6), "a");
   * original.put(new Point(8, 8), "b");
   * System.out.println(gson.toJson(original, type));
   * }
* * The above code prints this JSON object: * *
{@code
   * {
   *   "(5,6)": "a",
   *   "(8,8)": "b"
   * }
   * }
* *

Maps as JSON arrays * *

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":3}}. * *

Given the assumption above, a {@code Map} will be serialized as an array of * arrays (can be viewed as an entry set of pairs). * *

Below is an example of serializing complex types as JSON arrays: * *

{@code
   * Gson gson = new GsonBuilder()
   *     .enableComplexMapKeySerialization()
   *     .create();
   *
   * Map original = new LinkedHashMap<>();
   * 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: * *
{@code
   * [
   *   [
   *     {
   *       "x": 5,
   *       "y": 6
   *     },
   *     "a"
   *   ],
   *   [
   *     {
   *       "x": 8,
   *       "y": 8
   *     },
   *     "b"
   *   ]
   * ]
   * }
* * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern * @since 1.7 */ public GsonBuilder enableComplexMapKeySerialization() { complexMapKeySerialization = true; return this; } /** * Configures Gson to deserialize duplicate map keys. Only the value of last entry with the same key will be used, previous values * will be discarded. By default, Gson throws a {@link JsonSyntaxException} when a key occurs more than once. * *

Note that enabling support for duplicate maps keys is discouraged because it can make an application less secure. * When an application interacts with other components using different JSON libraries, they might treat duplicate keys * differently, allowing an attacker to circumvent security checks. * * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern * @since 2.8 */ public GsonBuilder enableDuplicateMapKeyDeserialization() { duplicateMapKeyDeserialization = true; return this; } /** * Configures Gson to exclude inner classes (= non-{@code static} nested classes) during * serialization and deserialization. This is a convenience method which behaves as if an {@link * ExclusionStrategy} which excludes inner classes was {@linkplain * #setExclusionStrategies(ExclusionStrategy...) registered with this builder}. This means inner * classes will be serialized as JSON {@code null}, and will be deserialized as Java {@code null} * with their JSON data being ignored. And fields with an inner class as type will be ignored * during serialization and deserialization. * *

By default Gson serializes and deserializes inner classes, but ignores references to the * enclosing instance. Deserialization might not be possible at all when {@link * #disableJdkUnsafe()} is used (and no custom {@link InstanceCreator} is registered), or it can * lead to unexpected {@code NullPointerException}s when the deserialized instance is used * afterwards. * *

In general using inner classes with Gson should be avoided; they should be converted to * {@code static} nested classes if possible. * * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern * @since 1.3 */ public GsonBuilder disableInnerClassSerialization() { excluder = excluder.disableInnerClassSerialization(); 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 = Objects.requireNonNull(serializationPolicy); return this; } /** * Configures Gson to apply a specific naming policy to an object's fields during serialization * and deserialization. * *

This method just delegates to {@link #setFieldNamingStrategy(FieldNamingStrategy)}. */ public GsonBuilder setFieldNamingPolicy(FieldNamingPolicy namingConvention) { return setFieldNamingStrategy(namingConvention); } /** * Configures Gson to apply a specific naming strategy to an object's fields during serialization * and deserialization. * *

The created Gson instance might only use the field naming strategy once for a field and * cache the result. It is not guaranteed that the strategy will be used again every time the * value of a field is serialized or deserialized. * * @param fieldNamingStrategy the 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) { this.fieldNamingPolicy = Objects.requireNonNull(fieldNamingStrategy); return this; } /** * Configures Gson to apply a specific number strategy during deserialization of {@link Object}. * * @param objectToNumberStrategy the actual object-to-number strategy * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern * @see ToNumberPolicy#DOUBLE The default object-to-number strategy * @since 2.8.9 */ public GsonBuilder setObjectToNumberStrategy(ToNumberStrategy objectToNumberStrategy) { this.objectToNumberStrategy = Objects.requireNonNull(objectToNumberStrategy); return this; } /** * Configures Gson to apply a specific number strategy during deserialization of {@link Number}. * * @param numberToNumberStrategy the actual number-to-number strategy * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern * @see ToNumberPolicy#LAZILY_PARSED_NUMBER The default number-to-number strategy * @since 2.8.9 */ public GsonBuilder setNumberToNumberStrategy(ToNumberStrategy numberToNumberStrategy) { this.numberToNumberStrategy = Objects.requireNonNull(numberToNumberStrategy); 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 serialization/deserialization. The strategies are * added to the existing strategies (if any); the existing strategies are not replaced. * *

Fields are excluded for serialization and deserialization when {@link * ExclusionStrategy#shouldSkipField(FieldAttributes) shouldSkipField} returns {@code true}, or * when {@link ExclusionStrategy#shouldSkipClass(Class) shouldSkipClass} returns {@code true} for * the field type. Gson behaves as if the field did not exist; its value is not serialized and on * deserialization if a JSON member with this name exists it is skipped by default.
* When objects of an excluded type (as determined by {@link * ExclusionStrategy#shouldSkipClass(Class) shouldSkipClass}) are serialized a JSON null is * written to output, and when deserialized the JSON value is skipped and {@code null} is * returned. * *

The created Gson instance might only use an exclusion strategy once for a field or class and * cache the result. It is not guaranteed that the strategy will be used again every time the * value of a field or a class is serialized or deserialized. * * @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) { Objects.requireNonNull(strategies); for (ExclusionStrategy strategy : strategies) { excluder = excluder.withExclusionStrategy(strategy, true, true); } 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. * *

See the documentation of {@link #setExclusionStrategies(ExclusionStrategy...)} for a * detailed description of the effect of exclusion strategies. * * @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) { Objects.requireNonNull(strategy); excluder = excluder.withExclusionStrategy(strategy, true, false); 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. * *

See the documentation of {@link #setExclusionStrategies(ExclusionStrategy...)} for a * detailed description of the effect of exclusion strategies. * * @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) { Objects.requireNonNull(strategy); excluder = excluder.withExclusionStrategy(strategy, false, true); return this; } /** * Configures Gson to output JSON that fits in a page for pretty printing. This option only * affects JSON serialization. * *

This is a convenience method which simply calls {@link #setFormattingStyle(FormattingStyle)} * with {@link FormattingStyle#PRETTY}. * * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern */ public GsonBuilder setPrettyPrinting() { return setFormattingStyle(FormattingStyle.PRETTY); } /** * Configures Gson to output JSON that uses a certain kind of formatting style (for example * newline and indent). This option only affects JSON serialization. By default Gson produces * compact JSON output without any formatting. * * @param formattingStyle the formatting style to use. * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern * @since $next-version$ */ public GsonBuilder setFormattingStyle(FormattingStyle formattingStyle) { this.formattingStyle = Objects.requireNonNull(formattingStyle); return this; } /** * Sets the strictness of this builder to {@link Strictness#LENIENT}. * * @deprecated This method is equivalent to calling {@link #setStrictness(Strictness)} with {@link * Strictness#LENIENT}: {@code setStrictness(Strictness.LENIENT)} * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern. * @see JsonReader#setStrictness(Strictness) * @see JsonWriter#setStrictness(Strictness) * @see #setStrictness(Strictness) */ @Deprecated public GsonBuilder setLenient() { return setStrictness(Strictness.LENIENT); } /** * Sets the strictness of this builder to the provided parameter. * *

This changes how strict the RFC 8259 JSON * specification is enforced when parsing or writing JSON. For details on this, refer to * {@link JsonReader#setStrictness(Strictness)} and {@link JsonWriter#setStrictness(Strictness)}. * * @param strictness the new strictness mode. May not be {@code null}. * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern. * @see JsonReader#setStrictness(Strictness) * @see JsonWriter#setStrictness(Strictness) * @since $next-version$ */ public GsonBuilder setStrictness(Strictness strictness) { this.strictness = Objects.requireNonNull(strictness); return this; } /** * By default, Gson always writes quotes around entry names. * This option allows omitting quotes if the name is alphanumeric. */ public GsonBuilder setOmitQuotes() { omitQuotes = 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, int)} multiple times, but only the last * invocation will be used to decide the serialization format. * *

The date format will be used to serialize and deserialize {@link java.util.Date} and in case * the {@code java.sql} module is present, also {@link java.sql.Timestamp} and {@link * java.sql.Date}. * *

Note that this pattern must abide by the convention provided by {@code SimpleDateFormat} * class. See the documentation in {@link SimpleDateFormat} for more information on valid date and * time patterns. * * @param pattern the pattern that dates will be serialized/deserialized to/from; can be {@code * null} to reset the pattern * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern * @throws IllegalArgumentException if the pattern is invalid * @since 1.2 */ public GsonBuilder setDateFormat(String pattern) { if (pattern != null) { try { new SimpleDateFormat(pattern); } catch (IllegalArgumentException e) { // Throw exception if it is an invalid date format throw new IllegalArgumentException("The date pattern '" + pattern + "' is not valid", e); } } this.datePattern = pattern; return this; } /** * Configures Gson to serialize {@code Date} objects according to the date 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. This methods leaves the current * 'time style' unchanged. * *

Note that this style value should be one of the predefined constants in the {@link * DateFormat} class, such as {@link DateFormat#MEDIUM}. See the documentation of the {@link * DateFormat} class for more information on the valid style constants. * * @deprecated Counterintuitively, despite this method taking only a 'date style' Gson will use a * format which includes both date and time, with the 'time style' being the last value set by * {@link #setDateFormat(int, int)}. Therefore prefer using {@link #setDateFormat(int, int)} * and explicitly provide the desired 'time style'. * @param dateStyle 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 * @throws IllegalArgumentException if the style is invalid * @since 1.2 */ @Deprecated public GsonBuilder setDateFormat(int dateStyle) { this.dateStyle = checkDateFormatStyle(dateStyle); this.datePattern = null; return this; } /** * Configures Gson 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. * *

Note that this style value should be one of the predefined constants in the {@link * DateFormat} class, such as {@link DateFormat#MEDIUM}. See the documentation of the {@link * DateFormat} class for more information on the valid style constants. * * @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 * @throws IllegalArgumentException if the style values are invalid * @since 1.2 */ public GsonBuilder setDateFormat(int dateStyle, int timeStyle) { this.dateStyle = checkDateFormatStyle(dateStyle); this.timeStyle = checkDateFormatStyle(timeStyle); this.datePattern = null; return this; } private static int checkDateFormatStyle(int style) { // Valid DateFormat styles are: 0, 1, 2, 3 (FULL, LONG, MEDIUM, SHORT) if (style < 0 || style > 3) { throw new IllegalArgumentException("Invalid style: " + style); } return style; } /** * Configures Gson for custom serialization or deserialization. This method combines the * registration of an {@link TypeAdapter}, {@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 a type adapter was * previously registered for the specified {@code type}, it is overwritten. * *

This registers the type specified and no other types: you must manually register related * types! For example, applications registering {@code boolean.class} should also register {@code * Boolean.class}. And when registering an adapter for a class which has subclasses, you might * also want to register the adapter for subclasses, or use {@link * #registerTypeHierarchyAdapter(Class, Object)} instead. * *

{@link JsonSerializer} and {@link JsonDeserializer} are made "{@code null}-safe". This means * when trying to serialize {@code null}, Gson will write a JSON {@code null} and the serializer * is not called. Similarly when deserializing a JSON {@code null}, Gson will emit {@code null} * without calling the deserializer. If it is desired to handle {@code null} values, a {@link * TypeAdapter} should be used instead. * * @param type the type definition for the type adapter being registered * @param typeAdapter This object must implement at least one of the {@link TypeAdapter}, {@link * InstanceCreator}, {@link JsonSerializer}, and a {@link JsonDeserializer} interfaces. * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern * @throws IllegalArgumentException if the type adapter being registered is for {@code Object} * class or {@link JsonElement} or any of its subclasses * @see #registerTypeHierarchyAdapter(Class, Object) */ public GsonBuilder registerTypeAdapter(Type type, Object typeAdapter) { Objects.requireNonNull(type); $Gson$Preconditions.checkArgument( typeAdapter instanceof JsonSerializer || typeAdapter instanceof JsonDeserializer || typeAdapter instanceof InstanceCreator || typeAdapter instanceof TypeAdapter); if (isTypeObjectOrJsonElement(type)) { throw new IllegalArgumentException("Cannot override built-in adapter for " + type); } if (typeAdapter instanceof InstanceCreator) { instanceCreators.put(type, (InstanceCreator) typeAdapter); } if (typeAdapter instanceof JsonSerializer || typeAdapter instanceof JsonDeserializer) { TypeToken typeToken = TypeToken.get(type); factories.add(TreeTypeAdapter.newFactoryWithMatchRawType(typeToken, typeAdapter)); } if (typeAdapter instanceof TypeAdapter) { @SuppressWarnings({"unchecked", "rawtypes"}) TypeAdapterFactory factory = TypeAdapters.newFactory(TypeToken.get(type), (TypeAdapter) typeAdapter); factories.add(factory); } return this; } private static boolean isTypeObjectOrJsonElement(Type type) { return type instanceof Class && (type == Object.class || JsonElement.class.isAssignableFrom((Class) type)); } /** * Registers a factory for type adapters. Registering a factory is useful when the type adapter * needs to be configured based on the type of the field being processed. Gson is designed to * handle a large number of factories, so you should consider registering them to be at par with * registering an individual type adapter. * *

The created Gson instance might only use the factory once to create an adapter for a * specific type and cache the result. It is not guaranteed that the factory will be used again * every time the type is serialized or deserialized. * * @since 2.1 */ public GsonBuilder registerTypeAdapterFactory(TypeAdapterFactory factory) { Objects.requireNonNull(factory); factories.add(factory); return this; } /** * Configures Gson for custom serialization or deserialization for an inheritance type hierarchy. * This method combines the registration of a {@link TypeAdapter}, {@link JsonSerializer} and a * {@link JsonDeserializer}. If a type adapter was previously registered for the specified type * hierarchy, it is overridden. If a type adapter 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 {@link TypeAdapter}, {@link * JsonSerializer} or {@link JsonDeserializer} interfaces. * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern * @throws IllegalArgumentException if the type adapter being registered is for {@link * JsonElement} or any of its subclasses * @since 1.7 */ public GsonBuilder registerTypeHierarchyAdapter(Class baseType, Object typeAdapter) { Objects.requireNonNull(baseType); $Gson$Preconditions.checkArgument( typeAdapter instanceof JsonSerializer || typeAdapter instanceof JsonDeserializer || typeAdapter instanceof TypeAdapter); if (JsonElement.class.isAssignableFrom(baseType)) { throw new IllegalArgumentException("Cannot override built-in adapter for " + baseType); } if (typeAdapter instanceof JsonDeserializer || typeAdapter instanceof JsonSerializer) { hierarchyFactories.add(TreeTypeAdapter.newTypeHierarchyFactory(baseType, typeAdapter)); } if (typeAdapter instanceof TypeAdapter) { @SuppressWarnings({"unchecked", "rawtypes"}) TypeAdapterFactory factory = TypeAdapters.newTypeHierarchyFactory(baseType, (TypeAdapter) typeAdapter); factories.add(factory); } return this; } /** * Section 6 of JSON specification disallows * 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 * 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 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; } /** * Disables usage of JDK's {@code sun.misc.Unsafe}. * *

By default Gson uses {@code Unsafe} to create instances of classes which don't have a * no-args constructor. However, {@code Unsafe} might not be available for all Java runtimes. For * example Android does not provide {@code Unsafe}, or only with limited functionality. * Additionally {@code Unsafe} creates instances without executing any constructor or initializer * block, or performing initialization of field values. This can lead to surprising and difficult * to debug errors. Therefore, to get reliable behavior regardless of which runtime is used, and * to detect classes which cannot be deserialized in an early stage of development, this method * allows disabling usage of {@code Unsafe}. * * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern * @since 2.9.0 */ public GsonBuilder disableJdkUnsafe() { this.useJdkUnsafe = false; return this; } /** * Adds a reflection access filter. A reflection access filter prevents Gson from using reflection * for the serialization and deserialization of certain classes. The logic in the filter specifies * which classes those are. * *

Filters will be invoked in reverse registration order, that is, the most recently added * filter will be invoked first. * *

By default Gson has no filters configured and will try to use reflection for all classes for * which no {@link TypeAdapter} has been registered, and for which no built-in Gson {@code * TypeAdapter} exists. * *

The created Gson instance might only use an access filter once for a class or its members * and cache the result. It is not guaranteed that the filter will be used again every time a * class or its members are accessed during serialization or deserialization. * * @param filter filter to add * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern * @since 2.9.1 */ public GsonBuilder addReflectionAccessFilter(ReflectionAccessFilter filter) { Objects.requireNonNull(filter); reflectionFilters.addFirst(filter); 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 factories = new ArrayList<>(this.factories.size() + this.hierarchyFactories.size() + 3); factories.addAll(this.factories); Collections.reverse(factories); List hierarchyFactories = new ArrayList<>(this.hierarchyFactories); Collections.reverse(hierarchyFactories); factories.addAll(hierarchyFactories); addTypeAdaptersForDate(datePattern, dateStyle, timeStyle, factories); return new Gson( excluder, fieldNamingPolicy, new HashMap<>(instanceCreators), serializeNulls, complexMapKeySerialization, duplicateMapKeyDeserialization, generateNonExecutableJson, escapeHtmlChars, formattingStyle, strictness, omitQuotes, serializeSpecialFloatingPointValues, useJdkUnsafe, longSerializationPolicy, datePattern, dateStyle, timeStyle, new ArrayList<>(this.factories), new ArrayList<>(this.hierarchyFactories), factories, objectToNumberStrategy, numberToNumberStrategy, new ArrayList<>(reflectionFilters)); } private static void addTypeAdaptersForDate( String datePattern, int dateStyle, int timeStyle, List factories) { TypeAdapterFactory dateAdapterFactory; boolean sqlTypesSupported = SqlTypesSupport.SUPPORTS_SQL_TYPES; TypeAdapterFactory sqlTimestampAdapterFactory = null; TypeAdapterFactory sqlDateAdapterFactory = null; if (datePattern != null && !datePattern.trim().isEmpty()) { dateAdapterFactory = DefaultDateTypeAdapter.DateType.DATE.createAdapterFactory(datePattern); if (sqlTypesSupported) { sqlTimestampAdapterFactory = SqlTypesSupport.TIMESTAMP_DATE_TYPE.createAdapterFactory(datePattern); sqlDateAdapterFactory = SqlTypesSupport.DATE_DATE_TYPE.createAdapterFactory(datePattern); } } else if (dateStyle != DateFormat.DEFAULT || timeStyle != DateFormat.DEFAULT) { dateAdapterFactory = DefaultDateTypeAdapter.DateType.DATE.createAdapterFactory(dateStyle, timeStyle); if (sqlTypesSupported) { sqlTimestampAdapterFactory = SqlTypesSupport.TIMESTAMP_DATE_TYPE.createAdapterFactory(dateStyle, timeStyle); sqlDateAdapterFactory = SqlTypesSupport.DATE_DATE_TYPE.createAdapterFactory(dateStyle, timeStyle); } } else { return; } factories.add(dateAdapterFactory); if (sqlTypesSupported) { factories.add(sqlTimestampAdapterFactory); factories.add(sqlDateAdapterFactory); } } }