diff --git a/src/main/java/org/json/JSONObject.java b/src/main/java/org/json/JSONObject.java index 82095468d..9c77eef60 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 less than 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); } /** diff --git a/src/test/java/org/json/junit/JSONObjectTest.java b/src/test/java/org/json/junit/JSONObjectTest.java index 061f18594..da0a0f3e1 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()); + } }