From a0ea108c59432da89ef0feb635ca3b4233440893 Mon Sep 17 00:00:00 2001 From: saddays Date: Wed, 21 Apr 2021 14:31:23 +0800 Subject: [PATCH 1/8] allow deserialize duplicate map key --- gson/src/main/java/com/google/gson/Gson.java | 9 ++++++--- .../main/java/com/google/gson/GsonBuilder.java | 17 ++++++++++++++++- .../internal/bind/MapTypeAdapterFactory.java | 8 +++++--- .../src/test/java/com/google/gson/GsonTest.java | 4 ++-- .../com/google/gson/functional/MapTest.java | 11 +++++++++++ 5 files changed, 40 insertions(+), 9 deletions(-) diff --git a/gson/src/main/java/com/google/gson/Gson.java b/gson/src/main/java/com/google/gson/Gson.java index 27f3ee92..c0e85a26 100644 --- a/gson/src/main/java/com/google/gson/Gson.java +++ b/gson/src/main/java/com/google/gson/Gson.java @@ -109,6 +109,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; private static final TypeToken NULL_KEY_SURROGATE = TypeToken.get(Object.class); @@ -136,6 +137,7 @@ public final class Gson { final Map> instanceCreators; final boolean serializeNulls; final boolean complexMapKeySerialization; + final boolean duplicateMapKeyDeserialization; final boolean generateNonExecutableJson; final boolean htmlSafe; final boolean prettyPrinting; @@ -185,7 +187,7 @@ public final class Gson { public Gson() { this(Excluder.DEFAULT, FieldNamingPolicy.IDENTITY, Collections.>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, LongSerializationPolicy.DEFAULT, null, DateFormat.DEFAULT, DateFormat.DEFAULT, Collections.emptyList(), Collections.emptyList(), @@ -194,7 +196,7 @@ public final class Gson { Gson(Excluder excluder, FieldNamingStrategy fieldNamingStrategy, Map> instanceCreators, boolean serializeNulls, - boolean complexMapKeySerialization, boolean generateNonExecutableGson, boolean htmlSafe, + boolean complexMapKeySerialization, boolean duplicateMapKeyDeserialization, boolean generateNonExecutableGson, boolean htmlSafe, boolean prettyPrinting, boolean lenient, boolean serializeSpecialFloatingPointValues, LongSerializationPolicy longSerializationPolicy, String datePattern, int dateStyle, int timeStyle, List builderFactories, @@ -206,6 +208,7 @@ public final class Gson { this.constructorConstructor = new ConstructorConstructor(instanceCreators); this.serializeNulls = serializeNulls; this.complexMapKeySerialization = complexMapKeySerialization; + this.duplicateMapKeyDeserialization = duplicateMapKeyDeserialization; this.generateNonExecutableJson = generateNonExecutableGson; this.htmlSafe = htmlSafe; this.prettyPrinting = prettyPrinting; @@ -270,7 +273,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); diff --git a/gson/src/main/java/com/google/gson/GsonBuilder.java b/gson/src/main/java/com/google/gson/GsonBuilder.java index b97be452..8b6399bc 100644 --- a/gson/src/main/java/com/google/gson/GsonBuilder.java +++ b/gson/src/main/java/com/google/gson/GsonBuilder.java @@ -34,6 +34,7 @@ import com.google.gson.reflect.TypeToken; import com.google.gson.stream.JsonReader; import static com.google.gson.Gson.DEFAULT_COMPLEX_MAP_KEYS; +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; @@ -89,6 +90,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; @@ -116,6 +118,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; @@ -275,6 +278,18 @@ public final class GsonBuilder { return this; } + /** + * Configure Gson to deserialize duplicate map key. By default, Gson throw a {@code JsonSyntaxException} when there are more than + * one same key. + * + * @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. * @@ -595,7 +610,7 @@ public final class GsonBuilder { addTypeAdaptersForDate(datePattern, dateStyle, timeStyle, factories); return new Gson(excluder, fieldNamingPolicy, instanceCreators, - serializeNulls, complexMapKeySerialization, + serializeNulls, complexMapKeySerialization, duplicateMapKeyDeserialization, generateNonExecutableJson, escapeHtmlChars, prettyPrinting, lenient, serializeSpecialFloatingPointValues, longSerializationPolicy, datePattern, dateStyle, timeStyle, diff --git a/gson/src/main/java/com/google/gson/internal/bind/MapTypeAdapterFactory.java b/gson/src/main/java/com/google/gson/internal/bind/MapTypeAdapterFactory.java index 5a34a5d5..6f259f20 100644 --- a/gson/src/main/java/com/google/gson/internal/bind/MapTypeAdapterFactory.java +++ b/gson/src/main/java/com/google/gson/internal/bind/MapTypeAdapterFactory.java @@ -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 TypeAdapter create(Gson gson, TypeToken typeToken) { @@ -173,7 +175,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(); @@ -186,7 +188,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); } } diff --git a/gson/src/test/java/com/google/gson/GsonTest.java b/gson/src/test/java/com/google/gson/GsonTest.java index eec2ec91..574ebbdd 100644 --- a/gson/src/test/java/com/google/gson/GsonTest.java +++ b/gson/src/test/java/com/google/gson/GsonTest.java @@ -46,7 +46,7 @@ public final class GsonTest extends TestCase { public void testOverridesDefaultExcluder() { Gson gson = new Gson(CUSTOM_EXCLUDER, CUSTOM_FIELD_NAMING_STRATEGY, - new HashMap>(), true, false, true, false, + new HashMap>(), true, false,false, true, false, true, true, false, LongSerializationPolicy.DEFAULT, null, DateFormat.DEFAULT, DateFormat.DEFAULT, new ArrayList(), new ArrayList(), new ArrayList()); @@ -59,7 +59,7 @@ public final class GsonTest extends TestCase { public void testClonedTypeAdapterFactoryListsAreIndependent() { Gson original = new Gson(CUSTOM_EXCLUDER, CUSTOM_FIELD_NAMING_STRATEGY, - new HashMap>(), true, false, true, false, + new HashMap>(), true, false,false, true, false, true, true, false, LongSerializationPolicy.DEFAULT, null, DateFormat.DEFAULT, DateFormat.DEFAULT, new ArrayList(), new ArrayList(), new ArrayList()); diff --git a/gson/src/test/java/com/google/gson/functional/MapTest.java b/gson/src/test/java/com/google/gson/functional/MapTest.java index d14585b3..5b890527 100644 --- a/gson/src/test/java/com/google/gson/functional/MapTest.java +++ b/gson/src/test/java/com/google/gson/functional/MapTest.java @@ -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>(){}.getType(); + String json = "{\"a\":1,\"b\":2,\"b\":3}"; + Map 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(); From 08765cae4c513b26c24ddf1e5647a179374f4088 Mon Sep 17 00:00:00 2001 From: tiegen Date: Sun, 25 Apr 2021 16:33:36 +0800 Subject: [PATCH 2/8] Update gson/src/main/java/com/google/gson/GsonBuilder.java Co-authored-by: Marcono1234 --- gson/src/main/java/com/google/gson/GsonBuilder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gson/src/main/java/com/google/gson/GsonBuilder.java b/gson/src/main/java/com/google/gson/GsonBuilder.java index 8b6399bc..4d00030c 100644 --- a/gson/src/main/java/com/google/gson/GsonBuilder.java +++ b/gson/src/main/java/com/google/gson/GsonBuilder.java @@ -285,7 +285,7 @@ public final class GsonBuilder { * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern * @since 2.8 */ - public GsonBuilder enableDuplicateMapKeyDeserialization() { + public GsonBuilder enableDuplicateMapKeysDeserialization() { duplicateMapKeyDeserialization = true; return this; } From 897452a3cfe29253596ca24b122caf4f57f60f2f Mon Sep 17 00:00:00 2001 From: tiegen Date: Sun, 25 Apr 2021 16:33:42 +0800 Subject: [PATCH 3/8] Update gson/src/main/java/com/google/gson/GsonBuilder.java Co-authored-by: Marcono1234 --- gson/src/main/java/com/google/gson/GsonBuilder.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/gson/src/main/java/com/google/gson/GsonBuilder.java b/gson/src/main/java/com/google/gson/GsonBuilder.java index 4d00030c..3188eb59 100644 --- a/gson/src/main/java/com/google/gson/GsonBuilder.java +++ b/gson/src/main/java/com/google/gson/GsonBuilder.java @@ -282,6 +282,10 @@ public final class GsonBuilder { * Configure Gson to deserialize duplicate map key. By default, Gson throw a {@code JsonSyntaxException} when there are more than * one same key. * + *

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 */ From ccf84849153046d86ef625e24b09bb6df3982164 Mon Sep 17 00:00:00 2001 From: tiegen Date: Sun, 25 Apr 2021 16:34:04 +0800 Subject: [PATCH 4/8] Update gson/src/main/java/com/google/gson/GsonBuilder.java Co-authored-by: Marcono1234 --- gson/src/main/java/com/google/gson/GsonBuilder.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gson/src/main/java/com/google/gson/GsonBuilder.java b/gson/src/main/java/com/google/gson/GsonBuilder.java index 3188eb59..c0401c19 100644 --- a/gson/src/main/java/com/google/gson/GsonBuilder.java +++ b/gson/src/main/java/com/google/gson/GsonBuilder.java @@ -279,8 +279,8 @@ public final class GsonBuilder { } /** - * Configure Gson to deserialize duplicate map key. By default, Gson throw a {@code JsonSyntaxException} when there are more than - * one same key. + * 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 From 8990169194f1b72504c2cc04027e6c1f59c15c17 Mon Sep 17 00:00:00 2001 From: tiegen Date: Sun, 25 Apr 2021 16:34:18 +0800 Subject: [PATCH 5/8] Update gson/src/test/java/com/google/gson/GsonTest.java Co-authored-by: Marcono1234 --- gson/src/test/java/com/google/gson/GsonTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gson/src/test/java/com/google/gson/GsonTest.java b/gson/src/test/java/com/google/gson/GsonTest.java index 574ebbdd..bf77885f 100644 --- a/gson/src/test/java/com/google/gson/GsonTest.java +++ b/gson/src/test/java/com/google/gson/GsonTest.java @@ -46,7 +46,7 @@ public final class GsonTest extends TestCase { public void testOverridesDefaultExcluder() { Gson gson = new Gson(CUSTOM_EXCLUDER, CUSTOM_FIELD_NAMING_STRATEGY, - new HashMap>(), true, false,false, true, false, + new HashMap>(), true, false, false, true, false, true, true, false, LongSerializationPolicy.DEFAULT, null, DateFormat.DEFAULT, DateFormat.DEFAULT, new ArrayList(), new ArrayList(), new ArrayList()); From e6b7da988ae4b80299e0936b3717b46f25bfdecc Mon Sep 17 00:00:00 2001 From: tiegen Date: Sun, 25 Apr 2021 16:34:26 +0800 Subject: [PATCH 6/8] Update gson/src/test/java/com/google/gson/GsonTest.java Co-authored-by: Marcono1234 --- gson/src/test/java/com/google/gson/GsonTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gson/src/test/java/com/google/gson/GsonTest.java b/gson/src/test/java/com/google/gson/GsonTest.java index bf77885f..c4da773a 100644 --- a/gson/src/test/java/com/google/gson/GsonTest.java +++ b/gson/src/test/java/com/google/gson/GsonTest.java @@ -59,7 +59,7 @@ public final class GsonTest extends TestCase { public void testClonedTypeAdapterFactoryListsAreIndependent() { Gson original = new Gson(CUSTOM_EXCLUDER, CUSTOM_FIELD_NAMING_STRATEGY, - new HashMap>(), true, false,false, true, false, + new HashMap>(), true, false, false, true, false, true, true, false, LongSerializationPolicy.DEFAULT, null, DateFormat.DEFAULT, DateFormat.DEFAULT, new ArrayList(), new ArrayList(), new ArrayList()); From 527c6cf0d0ced9ac00aa1e6f0fda393f6bb5be88 Mon Sep 17 00:00:00 2001 From: tiegen Date: Mon, 26 Apr 2021 10:36:02 +0800 Subject: [PATCH 7/8] Update gson/src/main/java/com/google/gson/GsonBuilder.java Co-authored-by: Marcono1234 --- gson/src/main/java/com/google/gson/GsonBuilder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gson/src/main/java/com/google/gson/GsonBuilder.java b/gson/src/main/java/com/google/gson/GsonBuilder.java index c0401c19..b8eb067e 100644 --- a/gson/src/main/java/com/google/gson/GsonBuilder.java +++ b/gson/src/main/java/com/google/gson/GsonBuilder.java @@ -280,7 +280,7 @@ public final class GsonBuilder { /** * 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. + * 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 From 9cf569698cefe77dfc09857f7b49b35197a6a31c Mon Sep 17 00:00:00 2001 From: tiegen Date: Mon, 26 Apr 2021 10:36:30 +0800 Subject: [PATCH 8/8] Update gson/src/main/java/com/google/gson/GsonBuilder.java Co-authored-by: Marcono1234 --- gson/src/main/java/com/google/gson/GsonBuilder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gson/src/main/java/com/google/gson/GsonBuilder.java b/gson/src/main/java/com/google/gson/GsonBuilder.java index b8eb067e..52bfdffa 100644 --- a/gson/src/main/java/com/google/gson/GsonBuilder.java +++ b/gson/src/main/java/com/google/gson/GsonBuilder.java @@ -289,7 +289,7 @@ public final class GsonBuilder { * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern * @since 2.8 */ - public GsonBuilder enableDuplicateMapKeysDeserialization() { + public GsonBuilder enableDuplicateMapKeyDeserialization() { duplicateMapKeyDeserialization = true; return this; }