Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
344 changes: 33 additions & 311 deletions gson/src/main/java/com/google/gson/Gson.java

Large diffs are not rendered by default.

255 changes: 185 additions & 70 deletions gson/src/main/java/com/google/gson/GsonBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,20 @@

package com.google.gson;

import static com.google.gson.Gson.DEFAULT_COMPLEX_MAP_KEYS;
import static com.google.gson.Gson.DEFAULT_DATE_PATTERN;
import static com.google.gson.Gson.DEFAULT_ESCAPE_HTML;
import static com.google.gson.Gson.DEFAULT_FORMATTING_STYLE;
import static com.google.gson.Gson.DEFAULT_JSON_NON_EXECUTABLE;
import static com.google.gson.Gson.DEFAULT_NUMBER_TO_NUMBER_STRATEGY;
import static com.google.gson.Gson.DEFAULT_OBJECT_TO_NUMBER_STRATEGY;
import static com.google.gson.Gson.DEFAULT_SERIALIZE_NULLS;
import static com.google.gson.Gson.DEFAULT_SPECIALIZE_FLOAT_VALUES;
import static com.google.gson.Gson.DEFAULT_STRICTNESS;
import static com.google.gson.Gson.DEFAULT_USE_JDK_UNSAFE;

import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.errorprone.annotations.InlineMe;
import com.google.gson.annotations.Since;
import com.google.gson.annotations.Until;
import com.google.gson.internal.ConstructorConstructor;
import com.google.gson.internal.Excluder;
import com.google.gson.internal.bind.ArrayTypeAdapter;
import com.google.gson.internal.bind.CollectionTypeAdapterFactory;
import com.google.gson.internal.bind.DefaultDateTypeAdapter;
import com.google.gson.internal.bind.JsonAdapterAnnotationTypeAdapterFactory;
import com.google.gson.internal.bind.MapTypeAdapterFactory;
import com.google.gson.internal.bind.NumberTypeAdapter;
import com.google.gson.internal.bind.ObjectTypeAdapter;
import com.google.gson.internal.bind.ReflectiveTypeAdapterFactory;
import com.google.gson.internal.bind.TreeTypeAdapter;
import com.google.gson.internal.bind.TypeAdapters;
import com.google.gson.internal.sql.SqlTypesSupport;
Expand All @@ -45,12 +41,16 @@
import java.text.SimpleDateFormat;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicLongArray;

/**
* Use this builder to construct a {@link Gson} instance when you need to set configuration options
Expand Down Expand Up @@ -90,29 +90,64 @@
* @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<Type, InstanceCreator<?>> instanceCreators = new HashMap<>();
private final List<TypeAdapterFactory> factories = new ArrayList<>();
private static final boolean DEFAULT_JSON_NON_EXECUTABLE = false;
// Strictness of `null` is the legacy mode where some Gson APIs are always lenient
private static final Strictness DEFAULT_STRICTNESS = null;
private static final FormattingStyle DEFAULT_FORMATTING_STYLE = FormattingStyle.COMPACT;
private static final boolean DEFAULT_ESCAPE_HTML = true;
private static final boolean DEFAULT_SERIALIZE_NULLS = false;
private static final boolean DEFAULT_COMPLEX_MAP_KEYS = false;
private static final boolean DEFAULT_SPECIALIZE_FLOAT_VALUES = false;
private static final boolean DEFAULT_USE_JDK_UNSAFE = true;
private static final String DEFAULT_DATE_PATTERN = null;
private static final FieldNamingStrategy DEFAULT_FIELD_NAMING_STRATEGY =
FieldNamingPolicy.IDENTITY;
private static final ToNumberStrategy DEFAULT_OBJECT_TO_NUMBER_STRATEGY = ToNumberPolicy.DOUBLE;
private static final ToNumberStrategy DEFAULT_NUMBER_TO_NUMBER_STRATEGY =
ToNumberPolicy.LAZILY_PARSED_NUMBER;

static final ConstructorConstructor DEFAULT_CONSTRUCTOR_CONSTRUCTOR =
new ConstructorConstructor(
Collections.emptyMap(), DEFAULT_USE_JDK_UNSAFE, Collections.emptyList());

static final JsonAdapterAnnotationTypeAdapterFactory
DEFAULT_JSON_ADAPTER_ANNOTATION_TYPE_ADAPTER_FACTORY =
new JsonAdapterAnnotationTypeAdapterFactory(DEFAULT_CONSTRUCTOR_CONSTRUCTOR);

/**
* Default instance of the builder, to be used only by the default {@link Gson#Gson()}
* constructor. Must not be used for anything else and must not be leaked to user code, since that
* could lead to accidental modification of this default builder.
*/
static final GsonBuilder DEFAULT = new GsonBuilder();

static final List<TypeAdapterFactory> DEFAULT_TYPE_ADAPTER_FACTORIES =
GsonBuilder.DEFAULT.createFactories(
DEFAULT_CONSTRUCTOR_CONSTRUCTOR, DEFAULT_JSON_ADAPTER_ANNOTATION_TYPE_ADAPTER_FACTORY);

Excluder excluder = Excluder.DEFAULT;
LongSerializationPolicy longSerializationPolicy = LongSerializationPolicy.DEFAULT;
FieldNamingStrategy fieldNamingPolicy = DEFAULT_FIELD_NAMING_STRATEGY;
final Map<Type, InstanceCreator<?>> instanceCreators = new HashMap<>();
final List<TypeAdapterFactory> factories = new ArrayList<>();

/** tree-style hierarchy factories. These come after factories for backwards compatibility. */
private final List<TypeAdapterFactory> 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 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 useJdkUnsafe = DEFAULT_USE_JDK_UNSAFE;
private ToNumberStrategy objectToNumberStrategy = DEFAULT_OBJECT_TO_NUMBER_STRATEGY;
private ToNumberStrategy numberToNumberStrategy = DEFAULT_NUMBER_TO_NUMBER_STRATEGY;
private final ArrayDeque<ReflectionAccessFilter> reflectionFilters = new ArrayDeque<>();
final List<TypeAdapterFactory> hierarchyFactories = new ArrayList<>();

boolean serializeNulls = DEFAULT_SERIALIZE_NULLS;
String datePattern = DEFAULT_DATE_PATTERN;
int dateStyle = DateFormat.DEFAULT;
int timeStyle = DateFormat.DEFAULT;
boolean complexMapKeySerialization = DEFAULT_COMPLEX_MAP_KEYS;
boolean serializeSpecialFloatingPointValues = DEFAULT_SPECIALIZE_FLOAT_VALUES;
boolean escapeHtmlChars = DEFAULT_ESCAPE_HTML;
FormattingStyle formattingStyle = DEFAULT_FORMATTING_STYLE;
boolean generateNonExecutableJson = DEFAULT_JSON_NON_EXECUTABLE;
Strictness strictness = DEFAULT_STRICTNESS;
boolean useJdkUnsafe = DEFAULT_USE_JDK_UNSAFE;
ToNumberStrategy objectToNumberStrategy = DEFAULT_OBJECT_TO_NUMBER_STRATEGY;
ToNumberStrategy numberToNumberStrategy = DEFAULT_NUMBER_TO_NUMBER_STRATEGY;
final ArrayDeque<ReflectionAccessFilter> reflectionFilters = new ArrayDeque<>();

/**
* Creates a GsonBuilder instance that can be used to build Gson with various configuration
Expand Down Expand Up @@ -884,43 +919,123 @@ public GsonBuilder addReflectionAccessFilter(ReflectionAccessFilter filter) {
* @return an instance of Gson configured with the options currently set in this builder
*/
public Gson create() {
List<TypeAdapterFactory> factories =
new ArrayList<>(this.factories.size() + this.hierarchyFactories.size() + 3);
factories.addAll(this.factories);
Collections.reverse(factories);

List<TypeAdapterFactory> 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,
generateNonExecutableJson,
escapeHtmlChars,
formattingStyle,
strictness,
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<TypeAdapterFactory> factories) {
return new Gson(this);
}

List<TypeAdapterFactory> createFactories(
ConstructorConstructor constructorConstructor,
JsonAdapterAnnotationTypeAdapterFactory jsonAdapterFactory) {
ArrayList<TypeAdapterFactory> factories = new ArrayList<>();

// built-in type adapters that cannot be overridden
factories.add(TypeAdapters.JSON_ELEMENT_FACTORY);
factories.add(ObjectTypeAdapter.getFactory(objectToNumberStrategy));

// the excluder must precede all adapters that handle user-defined types
factories.add(excluder);

// users' type adapters
addUserDefinedAdapters(factories);

// custom Date adapters
addDateTypeAdapters(factories);

// type adapters for basic platform types
factories.add(TypeAdapters.STRING_FACTORY);
factories.add(TypeAdapters.INTEGER_FACTORY);
factories.add(TypeAdapters.BOOLEAN_FACTORY);
factories.add(TypeAdapters.BYTE_FACTORY);
factories.add(TypeAdapters.SHORT_FACTORY);
TypeAdapter<Number> longAdapter = longSerializationPolicy.typeAdapter();
factories.add(TypeAdapters.newFactory(long.class, Long.class, longAdapter));
factories.add(TypeAdapters.newFactory(double.class, Double.class, doubleAdapter()));
factories.add(TypeAdapters.newFactory(float.class, Float.class, floatAdapter()));
factories.add(NumberTypeAdapter.getFactory(numberToNumberStrategy));
factories.add(TypeAdapters.ATOMIC_INTEGER_FACTORY);
factories.add(TypeAdapters.ATOMIC_BOOLEAN_FACTORY);
factories.add(
TypeAdapters.newFactory(AtomicLong.class, TypeAdapters.atomicLongAdapter(longAdapter)));
factories.add(
TypeAdapters.newFactory(
AtomicLongArray.class, TypeAdapters.atomicLongArrayAdapter(longAdapter)));
factories.add(TypeAdapters.ATOMIC_INTEGER_ARRAY_FACTORY);
factories.add(TypeAdapters.CHARACTER_FACTORY);
factories.add(TypeAdapters.STRING_BUILDER_FACTORY);
factories.add(TypeAdapters.STRING_BUFFER_FACTORY);
factories.add(TypeAdapters.BIG_DECIMAL_FACTORY);
factories.add(TypeAdapters.BIG_INTEGER_FACTORY);
// Add adapter for LazilyParsedNumber because user can obtain it from Gson and then try to
// serialize it again
factories.add(TypeAdapters.LAZILY_PARSED_NUMBER_FACTORY);
factories.add(TypeAdapters.URL_FACTORY);
factories.add(TypeAdapters.URI_FACTORY);
factories.add(TypeAdapters.UUID_FACTORY);
factories.add(TypeAdapters.CURRENCY_FACTORY);
factories.add(TypeAdapters.LOCALE_FACTORY);
factories.add(TypeAdapters.INET_ADDRESS_FACTORY);
factories.add(TypeAdapters.BIT_SET_FACTORY);
factories.add(DefaultDateTypeAdapter.DEFAULT_STYLE_FACTORY);
factories.add(TypeAdapters.CALENDAR_FACTORY);
factories.addAll(SqlTypesSupport.SQL_TYPE_FACTORIES);
factories.add(ArrayTypeAdapter.FACTORY);
factories.add(TypeAdapters.CLASS_FACTORY);

// type adapters for composite and user-defined types
factories.add(new CollectionTypeAdapterFactory(constructorConstructor));
factories.add(new MapTypeAdapterFactory(constructorConstructor, complexMapKeySerialization));
factories.add(jsonAdapterFactory);
factories.add(TypeAdapters.ENUM_FACTORY);
factories.add(
new ReflectiveTypeAdapterFactory(
constructorConstructor,
fieldNamingPolicy,
excluder,
jsonAdapterFactory,
newImmutableList(reflectionFilters)));

factories.trimToSize();
return Collections.unmodifiableList(factories);
}

static <E> List<E> newImmutableList(Collection<E> collection) {
if (collection.isEmpty()) {
return Collections.emptyList();
}
if (collection.size() == 1) {
return Collections.singletonList(
collection instanceof List
? ((List<E>) collection).get(0)
: collection.iterator().next());
}
@SuppressWarnings("unchecked")
List<E> list = (List<E>) Collections.unmodifiableList(Arrays.asList(collection.toArray()));
return list;
}

private TypeAdapter<Number> doubleAdapter() {
return serializeSpecialFloatingPointValues ? TypeAdapters.DOUBLE : TypeAdapters.DOUBLE_STRICT;
}

private TypeAdapter<Number> floatAdapter() {
return serializeSpecialFloatingPointValues ? TypeAdapters.FLOAT : TypeAdapters.FLOAT_STRICT;
}

private void addUserDefinedAdapters(List<TypeAdapterFactory> all) {
if (!this.factories.isEmpty()) {
List<TypeAdapterFactory> reversedFactories = new ArrayList<>(this.factories);
Collections.reverse(reversedFactories);
all.addAll(reversedFactories);
}

if (!this.hierarchyFactories.isEmpty()) {
List<TypeAdapterFactory> reversedHierarchyFactories =
new ArrayList<>(this.hierarchyFactories);
Collections.reverse(reversedHierarchyFactories);
all.addAll(reversedHierarchyFactories);
}
}

private void addDateTypeAdapters(List<TypeAdapterFactory> factories) {
TypeAdapterFactory dateAdapterFactory;
boolean sqlTypesSupported = SqlTypesSupport.SUPPORTS_SQL_TYPES;
TypeAdapterFactory sqlTimestampAdapterFactory = null;
Expand Down
16 changes: 16 additions & 0 deletions gson/src/main/java/com/google/gson/LongSerializationPolicy.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package com.google.gson;

import com.google.gson.internal.bind.TypeAdapters;

/**
* Defines the expected format for a {@code long} or {@code Long} type when it is serialized.
*
Expand All @@ -39,6 +41,11 @@ public JsonElement serialize(Long value) {
}
return new JsonPrimitive(value);
}

@Override
TypeAdapter<Number> typeAdapter() {
return TypeAdapters.LONG;
}
},

/**
Expand All @@ -55,6 +62,11 @@ public JsonElement serialize(Long value) {
}
return new JsonPrimitive(value.toString());
}

@Override
TypeAdapter<Number> typeAdapter() {
return TypeAdapters.LONG_AS_STRING;
}
};

/**
Expand All @@ -64,4 +76,8 @@ public JsonElement serialize(Long value) {
* @return the serialized version of {@code value}
*/
public abstract JsonElement serialize(Long value);

/** Returns the corresponding {@link TypeAdapter} for this serialization policy. */
// Internal method
abstract TypeAdapter<Number> typeAdapter();
}
Loading