Merge branch 'allow_duplicate_map_key'
# Conflicts: # gson/src/main/java/com/google/gson/GsonBuilder.java # gson/src/test/java/com/google/gson/GsonTest.java
This commit is contained in:
commit
9d7fd891ee
@ -110,6 +110,7 @@ public final class Gson {
|
||||
static final boolean DEFAULT_ESCAPE_HTML = true;
|
||||
static final boolean DEFAULT_SERIALIZE_NULLS = false;
|
||||
static final boolean DEFAULT_COMPLEX_MAP_KEYS = false;
|
||||
static final boolean DEFAULT_DUPLICATE_MAP_KEYS = false;
|
||||
static final boolean DEFAULT_SPECIALIZE_FLOAT_VALUES = false;
|
||||
static final boolean DEFAULT_USE_JDK_UNSAFE = true;
|
||||
static final String DEFAULT_DATE_PATTERN = null;
|
||||
@ -142,6 +143,7 @@ public final class Gson {
|
||||
final Map<Type, InstanceCreator<?>> instanceCreators;
|
||||
final boolean serializeNulls;
|
||||
final boolean complexMapKeySerialization;
|
||||
final boolean duplicateMapKeyDeserialization;
|
||||
final boolean generateNonExecutableJson;
|
||||
final boolean htmlSafe;
|
||||
final boolean prettyPrinting;
|
||||
@ -195,7 +197,7 @@ public final class Gson {
|
||||
public Gson() {
|
||||
this(Excluder.DEFAULT, DEFAULT_FIELD_NAMING_STRATEGY,
|
||||
Collections.<Type, InstanceCreator<?>>emptyMap(), DEFAULT_SERIALIZE_NULLS,
|
||||
DEFAULT_COMPLEX_MAP_KEYS, DEFAULT_JSON_NON_EXECUTABLE, DEFAULT_ESCAPE_HTML,
|
||||
DEFAULT_COMPLEX_MAP_KEYS, DEFAULT_DUPLICATE_MAP_KEYS, DEFAULT_JSON_NON_EXECUTABLE, DEFAULT_ESCAPE_HTML,
|
||||
DEFAULT_PRETTY_PRINT, DEFAULT_LENIENT, DEFAULT_SPECIALIZE_FLOAT_VALUES,
|
||||
DEFAULT_USE_JDK_UNSAFE,
|
||||
LongSerializationPolicy.DEFAULT, DEFAULT_DATE_PATTERN, DateFormat.DEFAULT, DateFormat.DEFAULT,
|
||||
@ -206,7 +208,7 @@ public final class Gson {
|
||||
|
||||
Gson(Excluder excluder, FieldNamingStrategy fieldNamingStrategy,
|
||||
Map<Type, InstanceCreator<?>> instanceCreators, boolean serializeNulls,
|
||||
boolean complexMapKeySerialization, boolean generateNonExecutableGson, boolean htmlSafe,
|
||||
boolean complexMapKeySerialization, boolean duplicateMapKeyDeserialization, boolean generateNonExecutableGson, boolean htmlSafe,
|
||||
boolean prettyPrinting, boolean lenient, boolean serializeSpecialFloatingPointValues,
|
||||
boolean useJdkUnsafe,
|
||||
LongSerializationPolicy longSerializationPolicy, String datePattern, int dateStyle,
|
||||
@ -221,6 +223,7 @@ public final class Gson {
|
||||
this.constructorConstructor = new ConstructorConstructor(instanceCreators, useJdkUnsafe, reflectionFilters);
|
||||
this.serializeNulls = serializeNulls;
|
||||
this.complexMapKeySerialization = complexMapKeySerialization;
|
||||
this.duplicateMapKeyDeserialization = duplicateMapKeyDeserialization;
|
||||
this.generateNonExecutableJson = generateNonExecutableGson;
|
||||
this.htmlSafe = htmlSafe;
|
||||
this.prettyPrinting = prettyPrinting;
|
||||
@ -295,7 +298,7 @@ public final class Gson {
|
||||
|
||||
// type adapters for composite and user-defined types
|
||||
factories.add(new CollectionTypeAdapterFactory(constructorConstructor));
|
||||
factories.add(new MapTypeAdapterFactory(constructorConstructor, complexMapKeySerialization));
|
||||
factories.add(new MapTypeAdapterFactory(constructorConstructor, complexMapKeySerialization, duplicateMapKeyDeserialization));
|
||||
this.jsonAdapterFactory = new JsonAdapterAnnotationTypeAdapterFactory(constructorConstructor);
|
||||
factories.add(jsonAdapterFactory);
|
||||
factories.add(TypeAdapters.ENUM_FACTORY);
|
||||
|
@ -37,6 +37,7 @@ import com.google.gson.stream.JsonReader;
|
||||
|
||||
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_DUPLICATE_MAP_KEYS;
|
||||
import static com.google.gson.Gson.DEFAULT_ESCAPE_HTML;
|
||||
import static com.google.gson.Gson.DEFAULT_JSON_NON_EXECUTABLE;
|
||||
import static com.google.gson.Gson.DEFAULT_LENIENT;
|
||||
@ -93,6 +94,7 @@ public final class GsonBuilder {
|
||||
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 boolean prettyPrinting = DEFAULT_PRETTY_PRINT;
|
||||
@ -124,6 +126,7 @@ public final class GsonBuilder {
|
||||
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.prettyPrinting = gson.prettyPrinting;
|
||||
@ -288,6 +291,22 @@ public final class GsonBuilder {
|
||||
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.
|
||||
*
|
||||
* <p>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 during serialization.
|
||||
*
|
||||
@ -674,7 +693,7 @@ public final class GsonBuilder {
|
||||
addTypeAdaptersForDate(datePattern, dateStyle, timeStyle, factories);
|
||||
|
||||
return new Gson(excluder, fieldNamingPolicy, new HashMap<>(instanceCreators),
|
||||
serializeNulls, complexMapKeySerialization,
|
||||
serializeNulls, complexMapKeySerialization, duplicateMapKeyDeserialization,
|
||||
generateNonExecutableJson, escapeHtmlChars, prettyPrinting, lenient,
|
||||
serializeSpecialFloatingPointValues, useJdkUnsafe, longSerializationPolicy,
|
||||
datePattern, dateStyle, timeStyle, new ArrayList<>(this.factories),
|
||||
|
@ -105,11 +105,13 @@ import java.util.Map;
|
||||
public final class MapTypeAdapterFactory implements TypeAdapterFactory {
|
||||
private final ConstructorConstructor constructorConstructor;
|
||||
final boolean complexMapKeySerialization;
|
||||
final boolean duplicateMapKeyDeserialization;
|
||||
|
||||
public MapTypeAdapterFactory(ConstructorConstructor constructorConstructor,
|
||||
boolean complexMapKeySerialization) {
|
||||
boolean complexMapKeySerialization, boolean duplicateMapKeyDeserialization) {
|
||||
this.constructorConstructor = constructorConstructor;
|
||||
this.complexMapKeySerialization = complexMapKeySerialization;
|
||||
this.duplicateMapKeyDeserialization = duplicateMapKeyDeserialization;
|
||||
}
|
||||
|
||||
@Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
|
||||
@ -172,7 +174,7 @@ public final class MapTypeAdapterFactory implements TypeAdapterFactory {
|
||||
K key = keyTypeAdapter.read(in);
|
||||
V value = valueTypeAdapter.read(in);
|
||||
V replaced = map.put(key, value);
|
||||
if (replaced != null) {
|
||||
if (!duplicateMapKeyDeserialization && replaced != null) {
|
||||
throw new JsonSyntaxException("duplicate key: " + key);
|
||||
}
|
||||
in.endArray();
|
||||
@ -185,7 +187,7 @@ public final class MapTypeAdapterFactory implements TypeAdapterFactory {
|
||||
K key = keyTypeAdapter.read(in);
|
||||
V value = valueTypeAdapter.read(in);
|
||||
V replaced = map.put(key, value);
|
||||
if (replaced != null) {
|
||||
if (!duplicateMapKeyDeserialization && replaced != null) {
|
||||
throw new JsonSyntaxException("duplicate key: " + key);
|
||||
}
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ public final class GsonTest extends TestCase {
|
||||
|
||||
public void testOverridesDefaultExcluder() {
|
||||
Gson gson = new Gson(CUSTOM_EXCLUDER, CUSTOM_FIELD_NAMING_STRATEGY,
|
||||
new HashMap<Type, InstanceCreator<?>>(), true, false, true, false,
|
||||
new HashMap<Type, InstanceCreator<?>>(), true, false, false, true, false,
|
||||
true, true, false, true, LongSerializationPolicy.DEFAULT, null, DateFormat.DEFAULT,
|
||||
DateFormat.DEFAULT, new ArrayList<TypeAdapterFactory>(),
|
||||
new ArrayList<TypeAdapterFactory>(), new ArrayList<TypeAdapterFactory>(),
|
||||
@ -68,7 +68,7 @@ public final class GsonTest extends TestCase {
|
||||
|
||||
public void testClonedTypeAdapterFactoryListsAreIndependent() {
|
||||
Gson original = new Gson(CUSTOM_EXCLUDER, CUSTOM_FIELD_NAMING_STRATEGY,
|
||||
new HashMap<Type, InstanceCreator<?>>(), true, false, true, false,
|
||||
new HashMap<Type, InstanceCreator<?>>(), true, false, false, true, false,
|
||||
true, true, false, true, LongSerializationPolicy.DEFAULT, null, DateFormat.DEFAULT,
|
||||
DateFormat.DEFAULT, new ArrayList<TypeAdapterFactory>(),
|
||||
new ArrayList<TypeAdapterFactory>(), new ArrayList<TypeAdapterFactory>(),
|
||||
|
@ -78,6 +78,17 @@ public class MapTest extends TestCase {
|
||||
assertEquals(2, target.get("b").intValue());
|
||||
}
|
||||
|
||||
public void testMapDuplicateKeyDeserialization() {
|
||||
Gson gsonWithDuplicateKeys = new GsonBuilder()
|
||||
.enableDuplicateMapKeyDeserialization()
|
||||
.create();
|
||||
Type typeOfMap = new TypeToken<Map<String,Integer>>(){}.getType();
|
||||
String json = "{\"a\":1,\"b\":2,\"b\":3}";
|
||||
Map<String,Integer> target = gsonWithDuplicateKeys.fromJson(json, typeOfMap);
|
||||
assertEquals(1, target.get("a").intValue());
|
||||
assertEquals(3, target.get("b").intValue());
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public void testRawMapSerialization() {
|
||||
Map map = new LinkedHashMap();
|
||||
|
Loading…
Reference in New Issue
Block a user