From 86f0b2ba8c698a0d71075cc8f131b39eb280547f Mon Sep 17 00:00:00 2001 From: Scott Paffrath <33729384+spaffrath@users.noreply.github.com> Date: Sun, 15 Jun 2025 07:42:13 -0700 Subject: [PATCH 1/4] newInternalMap --- src/main/java/org/json/JSONObject.java | 32 ++++++++++++++++++-------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/json/JSONObject.java b/src/main/java/org/json/JSONObject.java index 82095468d..2eaab7e05 100644 --- a/src/main/java/org/json/JSONObject.java +++ b/src/main/java/org/json/JSONObject.java @@ -135,6 +135,24 @@ public String toString() { */ private final Map map; + /** + * An internal factory method that can be overridden if the + * user desires JSONObject to use a specific map type. + *

+ * The default is HashMap to ensure that elements are unordered per the specification. + * JSON tends to be a portable transfer format that allows container + * implementations to rearrange their items for faster element + * retrieval based on associative access. + * Therefore, an implementation ought not rely on the order of items. + * @param capacity starting capacity. If < 0 then use the default capacity/constructor + * @return a new Map + */ + protected Map newInternalMap(int capacity) { + if (capacity < 0) + return new HashMap<>(); + return new HashMap<>(capacity); + } + /** * Retrieves the type of the underlying Map in this class. * @@ -156,13 +174,7 @@ public Class getMapType() { * Construct an empty JSONObject. */ public JSONObject() { - // HashMap is used on purpose to ensure that elements are unordered by - // the specification. - // JSON tends to be a portable transfer format to allows the container - // implementations to rearrange their items for a faster element - // retrieval based on associative access. - // Therefore, an implementation mustn't rely on the order of the item. - this.map = new HashMap(); + this.map = newInternalMap(-1); } /** @@ -324,9 +336,9 @@ private JSONObject(Map m, int recursionDepth, JSONParserConfiguration json throw new JSONException("JSONObject has reached recursion depth limit of " + jsonParserConfiguration.getMaxNestingDepth()); } if (m == null) { - this.map = new HashMap(); + this.map = newInternalMap(-1); } else { - this.map = new HashMap(m.size()); + this.map = newInternalMap(m.size()); for (final Entry e : m.entrySet()) { if(e.getKey() == null) { throw new NullPointerException("Null key."); @@ -525,7 +537,7 @@ public JSONObject(String baseName, Locale locale) throws JSONException { * @param initialCapacity initial capacity of the internal map. */ protected JSONObject(int initialCapacity){ - this.map = new HashMap(initialCapacity); + this.map = newInternalMap(initialCapacity); } /** From 7145c690bd94dfaea43a8311b02c096475403468 Mon Sep 17 00:00:00 2001 From: Scott Paffrath <33729384+spaffrath@users.noreply.github.com> Date: Sun, 13 Jul 2025 07:47:01 -0700 Subject: [PATCH 2/4] add test --- .../java/org/json/junit/JSONObjectTest.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/test/java/org/json/junit/JSONObjectTest.java b/src/test/java/org/json/junit/JSONObjectTest.java index 061f18594..dba3fc324 100644 --- a/src/test/java/org/json/junit/JSONObjectTest.java +++ b/src/test/java/org/json/junit/JSONObjectTest.java @@ -4044,4 +4044,21 @@ public void jsonObjectParseNullFieldsWithoutParserConfiguration() { assertTrue("JSONObject should be empty", jsonObject.isEmpty()); } + /** + * Testing that a custom map extension works. + */ + @Test + public void jsonObjectOrderedTest() { + JSONObject jsonObject = new JSONObject() { + @Override + public Map newInternalMap(int capacity) { + return new LinkedHashMap<>(); + } + }; + jsonObject.put("a", 1) + .put("c", 2) + .put("b", 3) + .put("d", 4); + assertEquals("{\"a\":1,\"c\":2,\"b\":3,\"d\":4}", jsonObject.toString()); + } } From a8276cde39e36b737d92a727c18067b70c47bcb4 Mon Sep 17 00:00:00 2001 From: Scott Paffrath <33729384+spaffrath@users.noreply.github.com> Date: Sun, 13 Jul 2025 13:29:10 -0700 Subject: [PATCH 3/4] Compatibility --- src/main/java/org/json/JSONObject.java | 4 ++-- src/test/java/org/json/junit/JSONObjectTest.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/json/JSONObject.java b/src/main/java/org/json/JSONObject.java index 2eaab7e05..fda0ba426 100644 --- a/src/main/java/org/json/JSONObject.java +++ b/src/main/java/org/json/JSONObject.java @@ -149,8 +149,8 @@ public String toString() { */ protected Map newInternalMap(int capacity) { if (capacity < 0) - return new HashMap<>(); - return new HashMap<>(capacity); + return new HashMap(); + return new HashMap(capacity); } /** diff --git a/src/test/java/org/json/junit/JSONObjectTest.java b/src/test/java/org/json/junit/JSONObjectTest.java index dba3fc324..da0a0f3e1 100644 --- a/src/test/java/org/json/junit/JSONObjectTest.java +++ b/src/test/java/org/json/junit/JSONObjectTest.java @@ -4052,7 +4052,7 @@ public void jsonObjectOrderedTest() { JSONObject jsonObject = new JSONObject() { @Override public Map newInternalMap(int capacity) { - return new LinkedHashMap<>(); + return new LinkedHashMap(); } }; jsonObject.put("a", 1) From c6d3bc2e29a6e6cbedf1b02d31a3befdae2e826c Mon Sep 17 00:00:00 2001 From: Scott Paffrath <33729384+spaffrath@users.noreply.github.com> Date: Sun, 13 Jul 2025 17:25:08 -0700 Subject: [PATCH 4/4] Javadoc compatibility --- src/main/java/org/json/JSONObject.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/json/JSONObject.java b/src/main/java/org/json/JSONObject.java index fda0ba426..9c77eef60 100644 --- a/src/main/java/org/json/JSONObject.java +++ b/src/main/java/org/json/JSONObject.java @@ -144,7 +144,7 @@ public String toString() { * implementations to rearrange their items for faster element * retrieval based on associative access. * Therefore, an implementation ought not rely on the order of items. - * @param capacity starting capacity. If < 0 then use the default capacity/constructor + * @param capacity starting capacity. If less than 0, then use the default capacity/constructor * @return a new Map */ protected Map newInternalMap(int capacity) {