From 0d8f29595bdf38c31e652ba87b1cf29be750361f Mon Sep 17 00:00:00 2001 From: lvca Date: Thu, 23 Dec 2021 11:43:57 -0500 Subject: [PATCH 1/3] Supported using custom map implementation for JSONObject, like a LinkedHashMap to keep the insertion order to the map fields. --- src/main/java/org/json/JSONObject.java | 13 +++++++++- .../java/org/json/junit/JSONObjectTest.java | 24 +++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/json/JSONObject.java b/src/main/java/org/json/JSONObject.java index 99a075069..71a3c6a2f 100644 --- a/src/main/java/org/json/JSONObject.java +++ b/src/main/java/org/json/JSONObject.java @@ -164,7 +164,7 @@ public String toString() { /** * The map where the JSONObject's properties are kept. */ - private final Map map; + protected final Map map; /** * It is sometimes more convenient and less ambiguous to have a @@ -367,6 +367,17 @@ public JSONObject(Object bean) { this.populateMap(bean); } + protected JSONObject(Class mapImplementation) { + if( mapImplementation == null ) + throw new JSONException("Map implementation is null"); + + try { + this.map = mapImplementation.getConstructor().newInstance(); + } catch (Exception e) { + throw new JSONException("Error on instantiating default constructor of map implementation from class '"+mapImplementation+"'"); + } + } + private JSONObject(Object bean, Set objectsRecord) { this(); this.populateMap(bean, objectsRecord); diff --git a/src/test/java/org/json/junit/JSONObjectTest.java b/src/test/java/org/json/junit/JSONObjectTest.java index 7f4fb72ad..05f3295a7 100644 --- a/src/test/java/org/json/junit/JSONObjectTest.java +++ b/src/test/java/org/json/junit/JSONObjectTest.java @@ -45,6 +45,7 @@ of this software and associated documentation files (the "Software"), to deal import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; import java.util.Map; @@ -3351,4 +3352,27 @@ public void jsonObjectClearMethodTest() { assertTrue("expected jsonObject.length() == 0", jsonObject.length() == 0); //Check if its length is 0 jsonObject.getInt("key1"); //Should throws org.json.JSONException: JSONObject["asd"] not found } + + + @Test + public void jsonObjectOrdered() { + class OrderedJSONObject extends JSONObject { + public OrderedJSONObject() { + super( LinkedHashMap.class ); + } + } + + JSONObject jsonObject = new OrderedJSONObject(); + for (int i = 0; i < 100; i++) + jsonObject.put(String.valueOf(i), i); + + // validate JSON + assertEquals("expected 100 items",100, jsonObject.length()); + + int i = 0; + for( String key : jsonObject.keySet() ){ + assertEquals("expected "+i+" as key",i, Integer.parseInt(key)); + ++i; + } + } } From 3e04ad2e42db6b2b3fd841cafb2a9b88179368cc Mon Sep 17 00:00:00 2001 From: lvca Date: Thu, 23 Dec 2021 15:25:18 -0500 Subject: [PATCH 2/3] Added support for custom map implementation in JSONTokener --- src/main/java/org/json/JSONObject.java | 32 ++++++------- src/main/java/org/json/JSONTokener.java | 12 +++++ .../java/org/json/junit/JSONTokenerTest.java | 45 ++++++++++++++----- 3 files changed, 62 insertions(+), 27 deletions(-) diff --git a/src/main/java/org/json/JSONObject.java b/src/main/java/org/json/JSONObject.java index 71a3c6a2f..b22aefb07 100644 --- a/src/main/java/org/json/JSONObject.java +++ b/src/main/java/org/json/JSONObject.java @@ -208,16 +208,16 @@ public JSONObject(JSONObject jo, String ... names) { } /** - * Construct a JSONObject from a JSONTokener. - * - * @param x - * A JSONTokener object containing the source string. - * @throws JSONException - * If there is a syntax error in the source string or a - * duplicated key. - */ + * Construct a JSONObject from a JSONTokener. + * + * @param x + * A JSONTokener object containing the source string. + * @throws JSONException + * If there is a syntax error in the source string or a + * duplicated key. + */ public JSONObject(JSONTokener x) throws JSONException { - this(); + this(x.getMapImplementation()); char c; String key; @@ -369,12 +369,14 @@ public JSONObject(Object bean) { protected JSONObject(Class mapImplementation) { if( mapImplementation == null ) - throw new JSONException("Map implementation is null"); - - try { - this.map = mapImplementation.getConstructor().newInstance(); - } catch (Exception e) { - throw new JSONException("Error on instantiating default constructor of map implementation from class '"+mapImplementation+"'"); + // Use the default implementation. + this.map = new HashMap(); + else { + try { + this.map = mapImplementation.getConstructor().newInstance(); + } catch (Exception e) { + throw new JSONException("Error on instantiating default constructor of map implementation from class '" + mapImplementation + "'"); + } } } diff --git a/src/main/java/org/json/JSONTokener.java b/src/main/java/org/json/JSONTokener.java index e6821de32..560c68c0b 100644 --- a/src/main/java/org/json/JSONTokener.java +++ b/src/main/java/org/json/JSONTokener.java @@ -6,6 +6,7 @@ import java.io.InputStreamReader; import java.io.Reader; import java.io.StringReader; +import java.util.Map; /* Copyright (c) 2002 JSON.org @@ -56,6 +57,8 @@ public class JSONTokener { /** the number of characters read in the previous line. */ private long characterPreviousLine; + /** custom map implementation to use. */ + private Class mapImplementation = null; /** * Construct a JSONTokener from a Reader. The caller must close the Reader. @@ -497,6 +500,15 @@ public char skipTo(char to) throws JSONException { return c; } + public Class getMapImplementation() { + return mapImplementation; + } + + public JSONTokener setMapImplementation(Class mapImplementation) { + this.mapImplementation = mapImplementation; + return this; + } + /** * Make a JSONException to signal a syntax error. * diff --git a/src/test/java/org/json/junit/JSONTokenerTest.java b/src/test/java/org/json/junit/JSONTokenerTest.java index e8e0f98a9..178edb93c 100644 --- a/src/test/java/org/json/junit/JSONTokenerTest.java +++ b/src/test/java/org/json/junit/JSONTokenerTest.java @@ -24,24 +24,20 @@ of this software and associated documentation files (the "Software"), to deal SOFTWARE. */ -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import java.io.BufferedReader; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.Reader; -import java.io.StringReader; - import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.json.JSONTokener; import org.junit.Test; +import java.io.*; +import java.util.*; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + /** * Test specific to the {@link org.json.JSONTokener} class. * @author John Aylward @@ -333,4 +329,29 @@ public void testNextBackComboWithNewLines() { assertEquals(0, t2.next()); assertFalse(t2.more()); } + + + @Test + public void jsonObjectOrdered() { + class OrderedJSONObject extends JSONObject { + public OrderedJSONObject() { + super( LinkedHashMap.class ); + } + } + + JSONObject jsonObject = new OrderedJSONObject(); + for (int i = 0; i < 100; i++) + jsonObject.put(String.valueOf(i), i); + + JSONObject orderedObject = new JSONObject( new JSONTokener(jsonObject.toString()).setMapImplementation( LinkedHashMap.class ) ); + + // validate JSON + assertEquals("expected 100 items",100, orderedObject.length()); + + int i = 0; + for( String key : orderedObject.keySet() ){ + assertEquals("expected "+i+" as key",i, Integer.parseInt(key)); + ++i; + } + } } From b88811db41d3eb257672ede1da2c0edba645d2c8 Mon Sep 17 00:00:00 2001 From: lvca Date: Thu, 23 Dec 2021 15:40:22 -0500 Subject: [PATCH 3/3] Fixed import --- src/test/java/org/json/junit/JSONTokenerTest.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/test/java/org/json/junit/JSONTokenerTest.java b/src/test/java/org/json/junit/JSONTokenerTest.java index 178edb93c..3eb53b391 100644 --- a/src/test/java/org/json/junit/JSONTokenerTest.java +++ b/src/test/java/org/json/junit/JSONTokenerTest.java @@ -30,8 +30,14 @@ of this software and associated documentation files (the "Software"), to deal import org.json.JSONTokener; import org.junit.Test; -import java.io.*; -import java.util.*; +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.StringReader; + +import java.util.LinkedHashMap; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -330,7 +336,6 @@ public void testNextBackComboWithNewLines() { assertFalse(t2.more()); } - @Test public void jsonObjectOrdered() { class OrderedJSONObject extends JSONObject {