diff --git a/README.md b/README.md index faf400a..0772d3b 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Unit tests to validate the JSON-Java GitHub project code
-https://github.com/douglascrockford/JSON-java
+https://github.com/stleary/JSON-java
Gradle and Eclipse is the recommended build tool and IDE.
Run individual tests or JunitTestSuite using EclEmma Coverage, or execute the **TestRunner** application directly.
diff --git a/src/test/java/org/json/junit/EnumTest.java b/src/test/java/org/json/junit/EnumTest.java index ab4a1e5..6b97107 100644 --- a/src/test/java/org/json/junit/EnumTest.java +++ b/src/test/java/org/json/junit/EnumTest.java @@ -5,7 +5,6 @@ import static org.junit.Assert.assertTrue; import java.util.EnumSet; import java.util.List; import java.util.Map; -import java.util.Set; import org.json.JSONArray; import org.json.JSONObject; @@ -92,7 +91,7 @@ public class EnumTest { assertTrue("expected 3 top level items", ((Map)(JsonPath.read(doc, "$"))).size() == 3); assertTrue("expected VAL1", MyEnumField.VAL1.equals(jsonObject.query("/VAL1"))); assertTrue("expected VAL2", MyEnumField.VAL2.equals(jsonObject.query("/VAL2"))); - assertTrue("expected VAL3", myEnumField.VAL3.equals(jsonObject.query("/VAL3"))); + assertTrue("expected VAL3", MyEnumField.VAL3.equals(jsonObject.query("/VAL3"))); } /** diff --git a/src/test/java/org/json/junit/JSONArrayTest.java b/src/test/java/org/json/junit/JSONArrayTest.java index 244a693..80b78a5 100644 --- a/src/test/java/org/json/junit/JSONArrayTest.java +++ b/src/test/java/org/json/junit/JSONArrayTest.java @@ -1,11 +1,11 @@ package org.json.junit; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import static org.junit.Assert.assertEquals; +import java.io.IOException; import java.io.StringWriter; -import java.io.Writer; import java.math.BigDecimal; import java.math.BigInteger; import java.util.ArrayList; @@ -61,7 +61,7 @@ public class JSONArrayTest { @Test(expected=NullPointerException.class) public void nullException() { String str = null; - new JSONArray(str); + assertNull("Should throw an exception", new JSONArray(str)); } /** @@ -72,8 +72,7 @@ public class JSONArrayTest { public void emptStr() { String str = ""; try { - new JSONArray(str); - assertTrue("Should throw an exception", false); + assertNull("Should throw an exception", new JSONArray(str)); } catch (JSONException e) { assertTrue("Expected an exception message", "A JSONArray text must start with '[' at 1 [character 2 line 1]". @@ -90,8 +89,7 @@ public class JSONArrayTest { public void badObject() { String str = "abc"; try { - new JSONArray((Object)str); - assertTrue("Should throw an exception", false); + assertNull("Should throw an exception", new JSONArray((Object)str)); } catch (JSONException e) { assertTrue("Expected an exception message", "JSONArray initial value should be a string or collection or array.". @@ -100,7 +98,7 @@ public class JSONArrayTest { } /** - * Verifies that the constructor has backwards compatability with RAW types pre-java5. + * Verifies that the constructor has backwards compatibility with RAW types pre-java5. */ @Test public void verifyConstructor() { @@ -130,7 +128,7 @@ public class JSONArrayTest { } /** - * Verifies that the put Collection has backwards compatability with RAW types pre-java5. + * Verifies that the put Collection has backwards compatibility with RAW types pre-java5. */ @Test public void verifyPutCollection() { @@ -164,7 +162,7 @@ public class JSONArrayTest { /** - * Verifies that the put Map has backwards compatability with RAW types pre-java5. + * Verifies that the put Map has backwards compatibility with RAW types pre-java5. */ @Test public void verifyPutMap() { @@ -209,9 +207,10 @@ public class JSONArrayTest { * Create a JSONArray doc with a variety of different elements. * Confirm that the values can be accessed via the get[type]() API methods */ + @SuppressWarnings("boxing") @Test public void getArrayValues() { - JSONArray jsonArray = new JSONArray(arrayStr); + JSONArray jsonArray = new JSONArray(this.arrayStr); // booleans assertTrue("Array true", true == jsonArray.getBoolean(0)); @@ -255,7 +254,7 @@ public class JSONArrayTest { */ @Test public void failedGetArrayValues() { - JSONArray jsonArray = new JSONArray(arrayStr); + JSONArray jsonArray = new JSONArray(this.arrayStr); try { jsonArray.getBoolean(4); assertTrue("expected getBoolean to fail", false); @@ -321,7 +320,7 @@ public class JSONArrayTest { */ @Test public void join() { - JSONArray jsonArray = new JSONArray(arrayStr); + JSONArray jsonArray = new JSONArray(this.arrayStr); String joinStr = jsonArray.join(","); // validate JSON @@ -357,7 +356,7 @@ public class JSONArrayTest { public void length() { assertTrue("expected empty JSONArray length 0", new JSONArray().length() == 0); - JSONArray jsonArray = new JSONArray(arrayStr); + JSONArray jsonArray = new JSONArray(this.arrayStr); assertTrue("expected JSONArray length 13", jsonArray.length() == 13); JSONArray nestedJsonArray = jsonArray.getJSONArray(9); assertTrue("expected JSONArray length 1", nestedJsonArray.length() == 1); @@ -368,9 +367,10 @@ public class JSONArrayTest { * Confirm that the values can be accessed via the opt[type](index) * and opt[type](index, default) API methods. */ + @SuppressWarnings("boxing") @Test public void opt() { - JSONArray jsonArray = new JSONArray(arrayStr); + JSONArray jsonArray = new JSONArray(this.arrayStr); assertTrue("Array opt value true", Boolean.TRUE == jsonArray.opt(0)); assertTrue("Array opt value out of range", @@ -441,6 +441,7 @@ public class JSONArrayTest { * Exercise the JSONArray.put(value) method with various parameters * and confirm the resulting JSONArray. */ + @SuppressWarnings("boxing") @Test public void put() { JSONArray jsonArray = new JSONArray(); @@ -516,6 +517,7 @@ public class JSONArrayTest { * Exercise the JSONArray.put(index, value) method with various parameters * and confirm the resulting JSONArray. */ + @SuppressWarnings("boxing") @Test public void putIndex() { JSONArray jsonArray = new JSONArray(); @@ -596,11 +598,11 @@ public class JSONArrayTest { */ @Test public void remove() { - String arrayStr = + String arrayStr1 = "["+ "1"+ "]"; - JSONArray jsonArray = new JSONArray(arrayStr); + JSONArray jsonArray = new JSONArray(arrayStr1); jsonArray.remove(0); assertTrue("array should be empty", null == jsonArray.remove(5)); assertTrue("jsonArray should be empty", jsonArray.length() == 0); @@ -612,11 +614,11 @@ public class JSONArrayTest { */ @Test public void notSimilar() { - String arrayStr = + String arrayStr1 = "["+ "1"+ "]"; - JSONArray jsonArray = new JSONArray(arrayStr); + JSONArray jsonArray = new JSONArray(arrayStr1); JSONArray otherJsonArray = new JSONArray(); assertTrue("arrays lengths differ", !jsonArray.similar(otherJsonArray)); @@ -745,9 +747,10 @@ public class JSONArrayTest { /** * Exercise the JSONArray iterator. */ + @SuppressWarnings("boxing") @Test public void iterator() { - JSONArray jsonArray = new JSONArray(arrayStr); + JSONArray jsonArray = new JSONArray(this.arrayStr); Iterator it = jsonArray.iterator(); assertTrue("Array true", Boolean.TRUE.equals(it.next())); @@ -803,16 +806,20 @@ public class JSONArrayTest { * Exercise the JSONArray write() method */ @Test - public void write() { + public void write() throws IOException { String str = "[\"value1\",\"value2\",{\"key1\":1,\"key2\":2,\"key3\":3}]"; JSONArray jsonArray = new JSONArray(str); String expectedStr = str; StringWriter stringWriter = new StringWriter(); - Writer writer = jsonArray.write(stringWriter); - String actualStr = writer.toString(); - assertTrue("write() expected " + expectedStr + - " but found " + actualStr, - expectedStr.equals(actualStr)); + try { + jsonArray.write(stringWriter); + String actualStr = stringWriter.toString(); + assertTrue("write() expected " + expectedStr + + " but found " + actualStr, + expectedStr.equals(actualStr)); + } finally { + stringWriter.close(); + } } /** @@ -837,7 +844,7 @@ public class JSONArrayTest { * Exercise the JSONArray write(Writer, int, int) method */ @Test - public void write3Param() { + public void write3Param() throws IOException { String str0 = "[\"value1\",\"value2\",{\"key1\":1,\"key2\":false,\"key3\":3.14}]"; String str2 = "[\n" + @@ -852,15 +859,20 @@ public class JSONArrayTest { JSONArray jsonArray = new JSONArray(str0); String expectedStr = str0; StringWriter stringWriter = new StringWriter(); - Writer writer = jsonArray.write(stringWriter, 0, 0); - String actualStr = writer.toString(); - assertEquals(expectedStr, actualStr); - - expectedStr = str2; + try { + String actualStr = jsonArray.write(stringWriter, 0, 0).toString(); + assertEquals(expectedStr, actualStr); + } finally { + stringWriter.close(); + } stringWriter = new StringWriter(); - writer = jsonArray.write(stringWriter, 2, 1); - actualStr = writer.toString(); - assertEquals(expectedStr, actualStr); + try { + expectedStr = str2; + String actualStr = jsonArray.write(stringWriter, 2, 1).toString(); + assertEquals(expectedStr, actualStr); + } finally { + stringWriter.close(); + } } /** @@ -917,49 +929,49 @@ public class JSONArrayTest { "]"; JSONArray jsonArray = new JSONArray(jsonArrayStr); - List list = jsonArray.toList(); + List list = jsonArray.toList(); assertTrue("List should not be null", list != null); assertTrue("List should have 3 elements", list.size() == 3); - List val1List = (List) list.get(0); + List val1List = (List) list.get(0); assertTrue("val1 should not be null", val1List != null); assertTrue("val1 should have 3 elements", val1List.size() == 3); assertTrue("val1 value 1 should be 1", val1List.get(0).equals(Integer.valueOf(1))); assertTrue("val1 value 2 should be 2", val1List.get(1).equals(Integer.valueOf(2))); - Map key1Value3Map = (Map)val1List.get(2); + Map key1Value3Map = (Map)val1List.get(2); assertTrue("Map should not be null", key1Value3Map != null); assertTrue("Map should have 1 element", key1Value3Map.size() == 1); assertTrue("Map key3 should be true", key1Value3Map.get("key3").equals(Boolean.TRUE)); - Map val2Map = (Map) list.get(1); + Map val2Map = (Map) list.get(1); assertTrue("val2 should not be null", val2Map != null); assertTrue("val2 should have 4 elements", val2Map.size() == 4); assertTrue("val2 map key 1 should be val1", val2Map.get("key1").equals("val1")); assertTrue("val2 map key 3 should be 42", val2Map.get("key3").equals(Integer.valueOf(42))); - Map val2Key2Map = (Map)val2Map.get("key2"); + Map val2Key2Map = (Map)val2Map.get("key2"); assertTrue("val2 map key 2 should not be null", val2Key2Map != null); assertTrue("val2 map key 2 should have an entry", val2Key2Map.containsKey("key2")); assertTrue("val2 map key 2 value should be null", val2Key2Map.get("key2") == null); - List val2Key4List = (List)val2Map.get("key4"); + List val2Key4List = (List)val2Map.get("key4"); assertTrue("val2 map key 4 should not be null", val2Key4List != null); assertTrue("val2 map key 4 should be empty", val2Key4List.isEmpty()); - List val3List = (List) list.get(2); + List val3List = (List) list.get(2); assertTrue("val3 should not be null", val3List != null); assertTrue("val3 should have 2 elements", val3List.size() == 2); - List val3Val1List = (List)val3List.get(0); + List val3Val1List = (List)val3List.get(0); assertTrue("val3 list val 1 should not be null", val3Val1List != null); assertTrue("val3 list val 1 should have 2 elements", val3Val1List.size() == 2); assertTrue("val3 list val 1 list element 1 should be value1", val3Val1List.get(0).equals("value1")); assertTrue("val3 list val 1 list element 2 should be 2.1", val3Val1List.get(1).equals(Double.valueOf("2.1"))); - List val3Val2List = (List)val3List.get(1); + List val3Val2List = (List)val3List.get(1); assertTrue("val3 list val 2 should not be null", val3Val2List != null); assertTrue("val3 list val 2 should have 1 element", val3Val2List.size() == 1); assertTrue("val3 list val 2 list element 1 should be null", val3Val2List.get(0) == null); diff --git a/src/test/java/org/json/junit/JSONMLTest.java b/src/test/java/org/json/junit/JSONMLTest.java index 1298591..8ece8e5 100644 --- a/src/test/java/org/json/junit/JSONMLTest.java +++ b/src/test/java/org/json/junit/JSONMLTest.java @@ -711,8 +711,7 @@ public class JSONMLTest { } /** - * JSON string cannot be reverted to original xml. See test result in - * comment below. + * JSON string cannot be reverted to original xml when type guessing is used. */ @Test public void testToJSONArray_reversibility() { @@ -722,10 +721,11 @@ public class JSONMLTest { } /** - * test passes when using the new method toJsonML. + * JSON string cannot be reverted to original xml when type guessing is used. + * When we force all the values as string, the original text comes back. */ @Test - public void testToJsonML() { + public void testToJSONArray_reversibility2() { final String originalXml = "011000True"; final String expectedJsonString = "[\"root\",[\"id\",\"01\"],[\"id\",\"1\"],[\"id\",\"00\"],[\"id\",\"0\"],[\"item\",{\"id\":\"01\"}],[\"title\",\"True\"]]"; final JSONArray json = JSONML.toJSONArray(originalXml,true); @@ -735,4 +735,72 @@ public class JSONMLTest { assertEquals(originalXml, reverseXml); } + /** + * JSON can be reverted to original xml. + */ + @Test + public void testToJSONArray_reversibility3() { + final String originalXml = "400402"; + final JSONArray jsonArray = JSONML.toJSONArray(originalXml, false); + final String revertedXml = JSONML.toString(jsonArray); + assertEquals(revertedXml, originalXml); + } + + /** + * JSON string cannot be reverted to original xml. See test result in + * comment below. + */ + @Test + public void testToJSONObject_reversibility() { + final String originalXml = "400402"; + final JSONObject originalObject=JSONML.toJSONObject(originalXml,false); + final String originalJson = originalObject.toString(); + final String xml = JSONML.toString(originalObject); + final JSONObject revertedObject = JSONML.toJSONObject(xml, false); + final String newJson = revertedObject.toString(); + assertTrue("JSON Objects are not similar",originalObject.similar(revertedObject)); + assertEquals("original JSON does not equal the new JSON",originalJson, newJson); + } + +// these tests do not pass for the following reasons: +// 1. Our XML parser does not handle generic HTML entities, only valid XML entities. Hence   +// or other HTML specific entities would fail on reversability +// 2. Our JSON implementation for storing the XML attributes uses the standard unordered map. +// This means that can not be reversed reliably. +// +// /** +// * Test texts taken from jsonml.org. Currently our implementation FAILS this conversion but shouldn't. +// * Technically JsonML should be able to transform any valid xhtml document, but ours only supports +// * standard XML entities, not HTML entities. +// */ +// @Test +// public void testAttributeConversionReversabilityHTML() { +// final String originalXml = "
#5D28D1Example text here
#AF44EF127310656
#AAD034 © 
"; +// final String expectedJsonString = "[\"table\",{\"class\" : \"MyTable\",\"style\" : \"background-color:yellow\"},[\"tr\",[\"td\",{\"class\" : \"MyTD\",\"style\" : \"border:1px solid black\"},\"#550758\"],[\"td\",{\"class\" : \"MyTD\",\"style\" : \"background-color:red\"},\"Example text here\"]],[\"tr\",[\"td\",{\"class\" : \"MyTD\",\"style\" : \"border:1px solid black\"},\"#993101\"],[\"td\",{\"class\" : \"MyTD\",\"style\" : \"background-color:green\"},\"127624015\"]],[\"tr\",[\"td\",{\"class\" : \"MyTD\",\"style\" : \"border:1px solid black\"},\"#E33D87\"],[\"td\",{\"class\" : \"MyTD\",\"style\" : \"background-color:blue\"},\"\u00A0\",[\"span\",{ \"style\" : \"background-color:maroon\" },\"\u00A9\"],\"\u00A0\"]]]"; +// final JSONArray json = JSONML.toJSONArray(originalXml,true); +// final String actualJsonString = json.toString(); +// +// final String reverseXml = JSONML.toString(json); +// assertNotEquals(originalXml, reverseXml); +// +// assertNotEquals(expectedJsonString, actualJsonString); +// } +// +// /** +// * Test texts taken from jsonml.org but modified to have XML entities only. +// */ +// @Test +// public void testAttributeConversionReversabilityXML() { +// final String originalXml = "
#5D28D1Example text here
#AF44EF127310656
#AAD034&><
"; +// final String expectedJsonString = "[\"table\",{\"class\" : \"MyTable\",\"style\" : \"background-color:yellow\"},[\"tr\",[\"td\",{\"class\" : \"MyTD\",\"style\" : \"border:1px solid black\"},\"#550758\"],[\"td\",{\"class\" : \"MyTD\",\"style\" : \"background-color:red\"},\"Example text here\"]],[\"tr\",[\"td\",{\"class\" : \"MyTD\",\"style\" : \"border:1px solid black\"},\"#993101\"],[\"td\",{\"class\" : \"MyTD\",\"style\" : \"background-color:green\"},\"127624015\"]],[\"tr\",[\"td\",{\"class\" : \"MyTD\",\"style\" : \"border:1px solid black\"},\"#E33D87\"],[\"td\",{\"class\" : \"MyTD\",\"style\" : \"background-color:blue\"},\"&\",[\"span\",{ \"style\" : \"background-color:maroon\" },\">\"],\"<\"]]]"; +// final JSONArray jsonML = JSONML.toJSONArray(originalXml,true); +// final String actualJsonString = jsonML.toString(); +// +// final String reverseXml = JSONML.toString(jsonML); +// // currently not equal because the hashing of the attribute objects makes the attribute +// // order not happen the same way twice +// assertEquals(originalXml, reverseXml); +// +// assertEquals(expectedJsonString, actualJsonString); +// } } diff --git a/src/test/java/org/json/junit/JSONObjectTest.java b/src/test/java/org/json/junit/JSONObjectTest.java index 1dec2fd..fb32cda 100644 --- a/src/test/java/org/json/junit/JSONObjectTest.java +++ b/src/test/java/org/json/junit/JSONObjectTest.java @@ -1,2336 +1,2371 @@ -package org.json.junit; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.io.StringReader; -import java.io.StringWriter; -import java.io.Writer; -import java.math.BigDecimal; -import java.math.BigInteger; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.concurrent.atomic.AtomicInteger; - -import org.json.CDL; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; -import org.json.JSONPointerException; -import org.json.XML; -import org.junit.Test; - -import com.jayway.jsonpath.Configuration; -import com.jayway.jsonpath.JsonPath; - -/** - * JSONObject, along with JSONArray, are the central classes of the reference app. - * All of the other classes interact with them, and JSON functionality would - * otherwise be impossible. - */ -public class JSONObjectTest { - - /** - * JSONObject built from a bean, but only using a null value. - * Nothing good is expected to happen. - * Expects NullPointerException - */ - @Test(expected=NullPointerException.class) - public void jsonObjectByNullBean() { - MyBean myBean = null; - new JSONObject(myBean); - } - - /** - * The JSON parser is permissive of unambiguous unquoted keys and values. - * Such JSON text should be allowed, even if it does not strictly conform - * to the spec. However, after being parsed, toString() should emit strictly - * conforming JSON text. - */ - @Test - public void unquotedText() { - String str = "{key1:value1, key2:42}"; - JSONObject jsonObject = new JSONObject(str); - String textStr = jsonObject.toString(); - assertTrue("expected key1", textStr.contains("\"key1\"")); - assertTrue("expected value1", textStr.contains("\"value1\"")); - assertTrue("expected key2", textStr.contains("\"key2\"")); - assertTrue("expected 42", textStr.contains("42")); - } - - /** - * A JSONObject can be created with no content - */ - @Test - public void emptyJsonObject() { - JSONObject jsonObject = new JSONObject(); - assertTrue("jsonObject should be empty", jsonObject.length() == 0); - } - - /** - * A JSONObject can be created from another JSONObject plus a list of names. - * In this test, some of the starting JSONObject keys are not in the - * names list. - */ - @Test - public void jsonObjectByNames() { - String str = - "{"+ - "\"trueKey\":true,"+ - "\"falseKey\":false,"+ - "\"nullKey\":null,"+ - "\"stringKey\":\"hello world!\","+ - "\"escapeStringKey\":\"h\be\tllo w\u1234orld!\","+ - "\"intKey\":42,"+ - "\"doubleKey\":-23.45e67"+ - "}"; - String[] keys = {"falseKey", "stringKey", "nullKey", "doubleKey"}; - JSONObject jsonObject = new JSONObject(str); - - // validate JSON - JSONObject jsonObjectByName = new JSONObject(jsonObject, keys); - Object doc = Configuration.defaultConfiguration().jsonProvider().parse(jsonObjectByName.toString()); - assertTrue("expected 4 top level items", ((Map)(JsonPath.read(doc, "$"))).size() == 4); - assertTrue("expected \"falseKey\":false", Boolean.FALSE.equals(jsonObjectByName.query("/falseKey"))); - assertTrue("expected \"nullKey\":null", JSONObject.NULL.equals(jsonObjectByName.query("/nullKey"))); - assertTrue("expected \"stringKey\":\"hello world!\"", "hello world!".equals(jsonObjectByName.query("/stringKey"))); - assertTrue("expected \"doubleKey\":-23.45e67", Double.valueOf("-23.45e67").equals(jsonObjectByName.query("/doubleKey"))); - } - - /** - * JSONObjects can be built from a Map. - * In this test the map is null. - * the JSONObject(JsonTokener) ctor is not tested directly since it already - * has full coverage from other tests. - */ - @Test - public void jsonObjectByNullMap() { - Map map = null; - JSONObject jsonObject = new JSONObject(map); - assertTrue("jsonObject should be empty", jsonObject.length() == 0); - } - - /** - * JSONObjects can be built from a Map. - * In this test all of the map entries are valid JSON types. - */ - @Test - public void jsonObjectByMap() { - Map map = new HashMap(); - map.put("trueKey", new Boolean(true)); - map.put("falseKey", new Boolean(false)); - map.put("stringKey", "hello world!"); - map.put("escapeStringKey", "h\be\tllo w\u1234orld!"); - map.put("intKey", new Long(42)); - map.put("doubleKey", new Double(-23.45e67)); - JSONObject jsonObject = new JSONObject(map); - - // validate JSON - Object doc = Configuration.defaultConfiguration().jsonProvider().parse(jsonObject.toString()); - assertTrue("expected 6 top level items", ((Map)(JsonPath.read(doc, "$"))).size() == 6); - assertTrue("expected \"trueKey\":true", Boolean.TRUE.equals(jsonObject.query("/trueKey"))); - assertTrue("expected \"falseKey\":false", Boolean.FALSE.equals(jsonObject.query("/falseKey"))); - assertTrue("expected \"stringKey\":\"hello world!\"", "hello world!".equals(jsonObject.query("/stringKey"))); - assertTrue("expected \"escapeStringKey\":\"h\be\tllo w\u1234orld!\"", "h\be\tllo w\u1234orld!".equals(jsonObject.query("/escapeStringKey"))); - assertTrue("expected \"doubleKey\":-23.45e67", Double.valueOf("-23.45e67").equals(jsonObject.query("/doubleKey"))); - } - - /** - * Verifies that the constructor has backwards compatability with RAW types pre-java5. - */ - @Test - public void verifyConstructor() { - - final JSONObject expected = new JSONObject("{\"myKey\":10}"); - - @SuppressWarnings("rawtypes") - Map myRawC = Collections.singletonMap("myKey", Integer.valueOf(10)); - JSONObject jaRaw = new JSONObject(myRawC); - - Map myCStrObj = Collections.singletonMap("myKey", - (Object) Integer.valueOf(10)); - JSONObject jaStrObj = new JSONObject(myCStrObj); - - Map myCStrInt = Collections.singletonMap("myKey", - Integer.valueOf(10)); - JSONObject jaStrInt = new JSONObject(myCStrInt); - - Map myCObjObj = Collections.singletonMap((Object) "myKey", - (Object) Integer.valueOf(10)); - JSONObject jaObjObj = new JSONObject(myCObjObj); - - assertTrue( - "The RAW Collection should give me the same as the Typed Collection", - expected.similar(jaRaw)); - assertTrue( - "The RAW Collection should give me the same as the Typed Collection", - expected.similar(jaStrObj)); - assertTrue( - "The RAW Collection should give me the same as the Typed Collection", - expected.similar(jaStrInt)); - assertTrue( - "The RAW Collection should give me the same as the Typed Collection", - expected.similar(jaObjObj)); - } - - /** - * Tests Number serialization. - */ - @Test - public void verifyNumberOutput(){ - /** - * MyNumberContainer is a POJO, so call JSONObject(bean), - * which builds a map of getter names/values - * The only getter is getMyNumber (key=myNumber), - * whose return value is MyNumber. MyNumber extends Number, - * but is not recognized as such by wrap() per current - * implementation, so wrap() returns the default new JSONObject(bean). - * The only getter is getNumber (key=number), whose return value is - * BigDecimal(42). - */ - JSONObject jsonObject = new JSONObject(new MyNumberContainer()); - String actual = jsonObject.toString(); - String expected = "{\"myNumber\":{\"number\":42}}"; - assertEquals("Not Equal", expected , actual); - - /** - * JSONObject.put() handles objects differently than the - * bean constructor. Where the bean ctor wraps objects before - * placing them in the map, put() inserts the object without wrapping. - * In this case, a MyNumber instance is the value. - * The MyNumber.toString() method is responsible for - * returning a reasonable value: the string '42'. - */ - jsonObject = new JSONObject(); - jsonObject.put("myNumber", new MyNumber()); - actual = jsonObject.toString(); - expected = "{\"myNumber\":42}"; - assertEquals("Not Equal", expected , actual); - - /** - * Calls the JSONObject(Map) ctor, which calls wrap() for values. - * AtomicInteger is a Number, but is not recognized by wrap(), per - * current implementation. However, the type is - * 'java.util.concurrent.atomic', so due to the 'java' prefix, - * wrap() inserts the value as a string. That is why 42 comes back - * wrapped in quotes. - */ - jsonObject = new JSONObject(Collections.singletonMap("myNumber", new AtomicInteger(42))); - actual = jsonObject.toString(); - expected = "{\"myNumber\":\"42\"}"; - assertEquals("Not Equal", expected , actual); - - /** - * JSONObject.put() inserts the AtomicInteger directly into the - * map not calling wrap(). In toString()->write()->writeValue(), - * AtomicInteger is recognized as a Number, and converted via - * numberToString() into the unquoted string '42'. - */ - jsonObject = new JSONObject(); - jsonObject.put("myNumber", new AtomicInteger(42)); - actual = jsonObject.toString(); - expected = "{\"myNumber\":42}"; - assertEquals("Not Equal", expected , actual); - - /** - * Calls the JSONObject(Map) ctor, which calls wrap() for values. - * Fraction is a Number, but is not recognized by wrap(), per - * current implementation. As a POJO, Franction is handled as a - * bean and inserted into a contained JSONObject. It has 2 getters, - * for numerator and denominator. - */ - jsonObject = new JSONObject(Collections.singletonMap("myNumber", new Fraction(4,2))); - assertEquals(1, jsonObject.length()); - assertEquals(2, ((JSONObject)(jsonObject.get("myNumber"))).length()); - assertEquals("Numerator", BigInteger.valueOf(4) , jsonObject.query("/myNumber/numerator")); - assertEquals("Denominator", BigInteger.valueOf(2) , jsonObject.query("/myNumber/denominator")); - - /** - * JSONObject.put() inserts the Fraction directly into the - * map not calling wrap(). In toString()->write()->writeValue(), - * Fraction is recognized as a Number, and converted via - * numberToString() into the unquoted string '4/2'. But the - * BigDecimal sanity check fails, so writeValue() defaults - * to returning a safe JSON quoted string. Pretty slick! - */ - jsonObject = new JSONObject(); - jsonObject.put("myNumber", new Fraction(4,2)); - actual = jsonObject.toString(); - expected = "{\"myNumber\":\"4/2\"}"; // valid JSON, bug fixed - assertEquals("Not Equal", expected , actual); - } - - /** - * Verifies that the put Collection has backwards compatability with RAW types pre-java5. - */ - @Test - public void verifyPutCollection() { - - final JSONObject expected = new JSONObject("{\"myCollection\":[10]}"); - - @SuppressWarnings("rawtypes") - Collection myRawC = Collections.singleton(Integer.valueOf(10)); - JSONObject jaRaw = new JSONObject(); - jaRaw.put("myCollection", myRawC); - - Collection myCObj = Collections.singleton((Object) Integer - .valueOf(10)); - JSONObject jaObj = new JSONObject(); - jaObj.put("myCollection", myCObj); - - Collection myCInt = Collections.singleton(Integer - .valueOf(10)); - JSONObject jaInt = new JSONObject(); - jaInt.put("myCollection", myCInt); - - assertTrue( - "The RAW Collection should give me the same as the Typed Collection", - expected.similar(jaRaw)); - assertTrue( - "The RAW Collection should give me the same as the Typed Collection", - expected.similar(jaObj)); - assertTrue( - "The RAW Collection should give me the same as the Typed Collection", - expected.similar(jaInt)); - } - - - /** - * Verifies that the put Map has backwards compatability with RAW types pre-java5. - */ - @Test - public void verifyPutMap() { - - final JSONObject expected = new JSONObject("{\"myMap\":{\"myKey\":10}}"); - - @SuppressWarnings("rawtypes") - Map myRawC = Collections.singletonMap("myKey", Integer.valueOf(10)); - JSONObject jaRaw = new JSONObject(); - jaRaw.put("myMap", myRawC); - - Map myCStrObj = Collections.singletonMap("myKey", - (Object) Integer.valueOf(10)); - JSONObject jaStrObj = new JSONObject(); - jaStrObj.put("myMap", myCStrObj); - - Map myCStrInt = Collections.singletonMap("myKey", - Integer.valueOf(10)); - JSONObject jaStrInt = new JSONObject(); - jaStrInt.put("myMap", myCStrInt); - - Map myCObjObj = Collections.singletonMap((Object) "myKey", - (Object) Integer.valueOf(10)); - JSONObject jaObjObj = new JSONObject(); - jaObjObj.put("myMap", myCObjObj); - - assertTrue( - "The RAW Collection should give me the same as the Typed Collection", - expected.similar(jaRaw)); - assertTrue( - "The RAW Collection should give me the same as the Typed Collection", - expected.similar(jaStrObj)); - assertTrue( - "The RAW Collection should give me the same as the Typed Collection", - expected.similar(jaStrInt)); - assertTrue( - "The RAW Collection should give me the same as the Typed Collection", - expected.similar(jaObjObj)); - } - - - /** - * JSONObjects can be built from a Map. - * In this test the map entries are not valid JSON types. - * The actual conversion is kind of interesting. - */ - @Test - public void jsonObjectByMapWithUnsupportedValues() { - Map jsonMap = new HashMap(); - // Just insert some random objects - jsonMap.put("key1", new CDL()); - jsonMap.put("key2", new Exception()); - - JSONObject jsonObject = new JSONObject(jsonMap); - - // validate JSON - Object doc = Configuration.defaultConfiguration().jsonProvider().parse(jsonObject.toString()); - assertTrue("expected 2 top level items", ((Map)(JsonPath.read(doc, "$"))).size() == 2); - assertTrue("expected 0 key1 items", ((Map)(JsonPath.read(doc, "$.key1"))).size() == 0); - assertTrue("expected \"key2\":java.lang.Exception","java.lang.Exception".equals(jsonObject.query("/key2"))); - } - - /** - * JSONObjects can be built from a Map. - * In this test one of the map values is null - */ - @Test - public void jsonObjectByMapWithNullValue() { - Map map = new HashMap(); - map.put("trueKey", new Boolean(true)); - map.put("falseKey", new Boolean(false)); - map.put("stringKey", "hello world!"); - map.put("nullKey", null); - map.put("escapeStringKey", "h\be\tllo w\u1234orld!"); - map.put("intKey", new Long(42)); - map.put("doubleKey", new Double(-23.45e67)); - JSONObject jsonObject = new JSONObject(map); - - // validate JSON - Object doc = Configuration.defaultConfiguration().jsonProvider().parse(jsonObject.toString()); - assertTrue("expected 6 top level items", ((Map)(JsonPath.read(doc, "$"))).size() == 6); - assertTrue("expected \"trueKey\":true", Boolean.TRUE.equals(jsonObject.query("/trueKey"))); - assertTrue("expected \"falseKey\":false", Boolean.FALSE.equals(jsonObject.query("/falseKey"))); - assertTrue("expected \"stringKey\":\"hello world!\"", "hello world!".equals(jsonObject.query("/stringKey"))); - assertTrue("expected \"escapeStringKey\":\"h\be\tllo w\u1234orld!\"", "h\be\tllo w\u1234orld!".equals(jsonObject.query("/escapeStringKey"))); - assertTrue("expected \"intKey\":42", Long.valueOf("42").equals(jsonObject.query("/intKey"))); - assertTrue("expected \"doubleKey\":-23.45e67", Double.valueOf("-23.45e67").equals(jsonObject.query("/doubleKey"))); - } - - /** - * JSONObject built from a bean. In this case all but one of the - * bean getters return valid JSON types - */ - @Test - public void jsonObjectByBean() { - /** - * Default access classes have to be mocked since JSONObject, which is - * not in the same package, cannot call MyBean methods by reflection. - */ - MyBean myBean = mock(MyBean.class); - when(myBean.getDoubleKey()).thenReturn(-23.45e7); - when(myBean.getIntKey()).thenReturn(42); - when(myBean.getStringKey()).thenReturn("hello world!"); - when(myBean.getEscapeStringKey()).thenReturn("h\be\tllo w\u1234orld!"); - when(myBean.isTrueKey()).thenReturn(true); - when(myBean.isFalseKey()).thenReturn(false); - when(myBean.getStringReaderKey()).thenReturn( - new StringReader("") { - }); - - JSONObject jsonObject = new JSONObject(myBean); - - // validate JSON - Object doc = Configuration.defaultConfiguration().jsonProvider().parse(jsonObject.toString()); - assertTrue("expected 8 top level items", ((Map)(JsonPath.read(doc, "$"))).size() == 8); - assertTrue("expected 0 items in stringReaderKey", ((Map) (JsonPath.read(doc, "$.stringReaderKey"))).size() == 0); - assertTrue("expected true", Boolean.TRUE.equals(jsonObject.query("/trueKey"))); - assertTrue("expected false", Boolean.FALSE.equals(jsonObject.query("/falseKey"))); - assertTrue("expected hello world!","hello world!".equals(jsonObject.query("/stringKey"))); - assertTrue("expected h\be\tllo w\u1234orld!", "h\be\tllo w\u1234orld!".equals(jsonObject.query("/escapeStringKey"))); - assertTrue("expected 42", Integer.valueOf("42").equals(jsonObject.query("/intKey"))); - assertTrue("expected -23.45e7", Double.valueOf("-23.45e7").equals(jsonObject.query("/doubleKey"))); - // sorry, mockito artifact - assertTrue("expected 2 callbacks items", ((List)(JsonPath.read(doc, "$.callbacks"))).size() == 2); - assertTrue("expected 0 handler items", ((Map)(JsonPath.read(doc, "$.callbacks[0].handler"))).size() == 0); - assertTrue("expected 0 callbacks[1] items", ((Map)(JsonPath.read(doc, "$.callbacks[1]"))).size() == 0); - } - - /** - * A bean is also an object. But in order to test the JSONObject - * ctor that takes an object and a list of names, - * this particular bean needs some public - * data members, which have been added to the class. - */ - @Test - public void jsonObjectByObjectAndNames() { - String[] keys = {"publicString", "publicInt"}; - // just need a class that has public data members - MyPublicClass myPublicClass = new MyPublicClass(); - JSONObject jsonObject = new JSONObject(myPublicClass, keys); - - // validate JSON - Object doc = Configuration.defaultConfiguration().jsonProvider().parse(jsonObject.toString()); - assertTrue("expected 2 top level items", ((Map)(JsonPath.read(doc, "$"))).size() == 2); - assertTrue("expected \"publicString\":\"abc\"", "abc".equals(jsonObject.query("/publicString"))); - assertTrue("expected \"publicInt\":42", Integer.valueOf(42).equals(jsonObject.query("/publicInt"))); - } - - /** - * Exercise the JSONObject from resource bundle functionality. - * The test resource bundle is uncomplicated, but provides adequate test coverage. - */ - @Test - public void jsonObjectByResourceBundle() { - JSONObject jsonObject = new - JSONObject("org.json.junit.StringsResourceBundle", - Locale.getDefault()); - - // validate JSON - Object doc = Configuration.defaultConfiguration().jsonProvider().parse(jsonObject.toString()); - assertTrue("expected 2 top level items", ((Map)(JsonPath.read(doc, "$"))).size() == 2); - assertTrue("expected 2 greetings items", ((Map)(JsonPath.read(doc, "$.greetings"))).size() == 2); - assertTrue("expected \"hello\":\"Hello, \"", "Hello, ".equals(jsonObject.query("/greetings/hello"))); - assertTrue("expected \"world\":\"World!\"", "World!".equals(jsonObject.query("/greetings/world"))); - assertTrue("expected 2 farewells items", ((Map)(JsonPath.read(doc, "$.farewells"))).size() == 2); - assertTrue("expected \"later\":\"Later, \"", "Later, ".equals(jsonObject.query("/farewells/later"))); - assertTrue("expected \"world\":\"World!\"", "Alligator!".equals(jsonObject.query("/farewells/gator"))); - } - - /** - * Exercise the JSONObject.accumulate() method - */ - @Test - public void jsonObjectAccumulate() { - - JSONObject jsonObject = new JSONObject(); - jsonObject.accumulate("myArray", true); - jsonObject.accumulate("myArray", false); - jsonObject.accumulate("myArray", "hello world!"); - jsonObject.accumulate("myArray", "h\be\tllo w\u1234orld!"); - jsonObject.accumulate("myArray", 42); - jsonObject.accumulate("myArray", -23.45e7); - // include an unsupported object for coverage - try { - jsonObject.accumulate("myArray", Double.NaN); - assertTrue("Expected exception", false); - } catch (JSONException ignored) {} - - // validate JSON - Object doc = Configuration.defaultConfiguration().jsonProvider().parse(jsonObject.toString()); - assertTrue("expected 1 top level item", ((Map)(JsonPath.read(doc, "$"))).size() == 1); - assertTrue("expected 6 myArray items", ((List)(JsonPath.read(doc, "$.myArray"))).size() == 6); - assertTrue("expected true", Boolean.TRUE.equals(jsonObject.query("/myArray/0"))); - assertTrue("expected false", Boolean.FALSE.equals(jsonObject.query("/myArray/1"))); - assertTrue("expected hello world!", "hello world!".equals(jsonObject.query("/myArray/2"))); - assertTrue("expected h\be\tllo w\u1234orld!", "h\be\tllo w\u1234orld!".equals(jsonObject.query("/myArray/3"))); - assertTrue("expected 42", Integer.valueOf(42).equals(jsonObject.query("/myArray/4"))); - assertTrue("expected -23.45e7", Double.valueOf(-23.45e7).equals(jsonObject.query("/myArray/5"))); - } - - /** - * Exercise the JSONObject append() functionality - */ - @Test - public void jsonObjectAppend() { - JSONObject jsonObject = new JSONObject(); - jsonObject.append("myArray", true); - jsonObject.append("myArray", false); - jsonObject.append("myArray", "hello world!"); - jsonObject.append("myArray", "h\be\tllo w\u1234orld!"); - jsonObject.append("myArray", 42); - jsonObject.append("myArray", -23.45e7); - // include an unsupported object for coverage - try { - jsonObject.append("myArray", Double.NaN); - assertTrue("Expected exception", false); - } catch (JSONException ignored) {} - - // validate JSON - Object doc = Configuration.defaultConfiguration().jsonProvider().parse(jsonObject.toString()); - assertTrue("expected 1 top level item", ((Map)(JsonPath.read(doc, "$"))).size() == 1); - assertTrue("expected 6 myArray items", ((List)(JsonPath.read(doc, "$.myArray"))).size() == 6); - assertTrue("expected true", Boolean.TRUE.equals(jsonObject.query("/myArray/0"))); - assertTrue("expected false", Boolean.FALSE.equals(jsonObject.query("/myArray/1/"))); - assertTrue("expected hello world!", "hello world!".equals(jsonObject.query("/myArray/2"))); - assertTrue("expected h\be\tllo w\u1234orld!", "h\be\tllo w\u1234orld!".equals(jsonObject.query("/myArray/3"))); - assertTrue("expected 42", Integer.valueOf(42).equals(jsonObject.query("/myArray/4"))); - assertTrue("expected -23.45e7", Double.valueOf(-23.45e7).equals(jsonObject.query("/myArray/5"))); - } - - /** - * Exercise the JSONObject doubleToString() method - */ - @Test - public void jsonObjectDoubleToString() { - String [] expectedStrs = {"1", "1", "-23.4", "-2.345E68", "null", "null" }; - Double [] doubles = { 1.0, 00001.00000, -23.4, -23.45e67, - Double.NaN, Double.NEGATIVE_INFINITY }; - for (int i = 0; i < expectedStrs.length; ++i) { - String actualStr = JSONObject.doubleToString(doubles[i]); - assertTrue("value expected ["+expectedStrs[i]+ - "] found ["+actualStr+ "]", - expectedStrs[i].equals(actualStr)); - } - } - - /** - * Exercise some JSONObject get[type] and opt[type] methods - */ - @Test - public void jsonObjectValues() { - String str = - "{"+ - "\"trueKey\":true,"+ - "\"falseKey\":false,"+ - "\"trueStrKey\":\"true\","+ - "\"falseStrKey\":\"false\","+ - "\"stringKey\":\"hello world!\","+ - "\"intKey\":42,"+ - "\"intStrKey\":\"43\","+ - "\"longKey\":1234567890123456789,"+ - "\"longStrKey\":\"987654321098765432\","+ - "\"doubleKey\":-23.45e7,"+ - "\"doubleStrKey\":\"00001.000\","+ - "\"arrayKey\":[0,1,2],"+ - "\"objectKey\":{\"myKey\":\"myVal\"}"+ - "}"; - JSONObject jsonObject = new JSONObject(str); - assertTrue("trueKey should be true", jsonObject.getBoolean("trueKey")); - assertTrue("opt trueKey should be true", jsonObject.optBoolean("trueKey")); - assertTrue("falseKey should be false", !jsonObject.getBoolean("falseKey")); - assertTrue("trueStrKey should be true", jsonObject.getBoolean("trueStrKey")); - assertTrue("trueStrKey should be true", jsonObject.optBoolean("trueStrKey")); - assertTrue("falseStrKey should be false", !jsonObject.getBoolean("falseStrKey")); - assertTrue("stringKey should be string", - jsonObject.getString("stringKey").equals("hello world!")); - assertTrue("doubleKey should be double", - jsonObject.getDouble("doubleKey") == -23.45e7); - assertTrue("doubleStrKey should be double", - jsonObject.getDouble("doubleStrKey") == 1); - assertTrue("opt doubleKey should be double", - jsonObject.optDouble("doubleKey") == -23.45e7); - assertTrue("opt doubleKey with Default should be double", - jsonObject.optDouble("doubleStrKey", Double.NaN) == 1); - assertTrue("intKey should be int", - jsonObject.optInt("intKey") == 42); - assertTrue("opt intKey should be int", - jsonObject.optInt("intKey", 0) == 42); - assertTrue("opt intKey with default should be int", - jsonObject.getInt("intKey") == 42); - assertTrue("intStrKey should be int", - jsonObject.getInt("intStrKey") == 43); - assertTrue("longKey should be long", - jsonObject.getLong("longKey") == 1234567890123456789L); - assertTrue("opt longKey should be long", - jsonObject.optLong("longKey") == 1234567890123456789L); - assertTrue("opt longKey with default should be long", - jsonObject.optLong("longKey", 0) == 1234567890123456789L); - assertTrue("longStrKey should be long", - jsonObject.getLong("longStrKey") == 987654321098765432L); - assertTrue("xKey should not exist", - jsonObject.isNull("xKey")); - assertTrue("stringKey should exist", - jsonObject.has("stringKey")); - assertTrue("opt stringKey should string", - jsonObject.optString("stringKey").equals("hello world!")); - assertTrue("opt stringKey with default should string", - jsonObject.optString("stringKey", "not found").equals("hello world!")); - JSONArray jsonArray = jsonObject.getJSONArray("arrayKey"); - assertTrue("arrayKey should be JSONArray", - jsonArray.getInt(0) == 0 && - jsonArray.getInt(1) == 1 && - jsonArray.getInt(2) == 2); - jsonArray = jsonObject.optJSONArray("arrayKey"); - assertTrue("opt arrayKey should be JSONArray", - jsonArray.getInt(0) == 0 && - jsonArray.getInt(1) == 1 && - jsonArray.getInt(2) == 2); - JSONObject jsonObjectInner = jsonObject.getJSONObject("objectKey"); - assertTrue("objectKey should be JSONObject", - jsonObjectInner.get("myKey").equals("myVal")); - } - - /** - * Check whether JSONObject handles large or high precision numbers correctly - */ - @Test - public void stringToValueNumbersTest() { - assertTrue("-0 Should be a Double!",JSONObject.stringToValue("-0") instanceof Double); - assertTrue("-0.0 Should be a Double!",JSONObject.stringToValue("-0.0") instanceof Double); - assertTrue("'-' Should be a String!",JSONObject.stringToValue("-") instanceof String); - assertTrue( "0.2 should be a Double!", - JSONObject.stringToValue( "0.2" ) instanceof Double ); - assertTrue( "Doubles should be Doubles, even when incorrectly converting floats!", - JSONObject.stringToValue( new Double( "0.2f" ).toString() ) instanceof Double ); - /** - * This test documents a need for BigDecimal conversion. - */ - Object obj = JSONObject.stringToValue( "299792.457999999984" ); - assertTrue( "evaluates to 299792.458 doubld instead of 299792.457999999984 BigDecimal!", - obj.equals(new Double(299792.458)) ); - assertTrue( "1 should be an Integer!", - JSONObject.stringToValue( "1" ) instanceof Integer ); - assertTrue( "Integer.MAX_VALUE should still be an Integer!", - JSONObject.stringToValue( new Integer( Integer.MAX_VALUE ).toString() ) instanceof Integer ); - assertTrue( "Large integers should be a Long!", - JSONObject.stringToValue( new Long( Long.sum( Integer.MAX_VALUE, 1 ) ).toString() ) instanceof Long ); - assertTrue( "Long.MAX_VALUE should still be an Integer!", - JSONObject.stringToValue( new Long( Long.MAX_VALUE ).toString() ) instanceof Long ); - - String str = new BigInteger( new Long( Long.MAX_VALUE ).toString() ).add( BigInteger.ONE ).toString(); - assertTrue( "Really large integers currently evaluate to string", - JSONObject.stringToValue(str).equals("9223372036854775808")); - } - - /** - * This test documents numeric values which could be numerically - * handled as BigDecimal or BigInteger. It helps determine what outputs - * will change if those types are supported. - */ - @Test - public void jsonValidNumberValuesNeitherLongNorIEEE754Compatible() { - // Valid JSON Numbers, probably should return BigDecimal or BigInteger objects - String str = - "{"+ - "\"numberWithDecimals\":299792.457999999984,"+ - "\"largeNumber\":12345678901234567890,"+ - "\"preciseNumber\":0.2000000000000000111,"+ - "\"largeExponent\":-23.45e2327"+ - "}"; - JSONObject jsonObject = new JSONObject(str); - // Comes back as a double, but loses precision - assertTrue( "numberWithDecimals currently evaluates to double 299792.458", - jsonObject.get( "numberWithDecimals" ).equals( new Double( "299792.458" ) ) ); - Object obj = jsonObject.get( "largeNumber" ); - assertTrue("largeNumber currently evaluates to string", - "12345678901234567890".equals(obj)); - // comes back as a double but loses precision - assertTrue( "preciseNumber currently evaluates to double 0.2", - jsonObject.get( "preciseNumber" ).equals(new Double(0.2))); - obj = jsonObject.get( "largeExponent" ); - assertTrue("largeExponent should currently evaluates as a string", - "-23.45e2327".equals(obj)); - } - - /** - * This test documents how JSON-Java handles invalid numeric input. - */ - @Test - public void jsonInvalidNumberValues() { - // Number-notations supported by Java and invalid as JSON - String str = - "{"+ - "\"hexNumber\":-0x123,"+ - "\"tooManyZeros\":00,"+ - "\"negativeInfinite\":-Infinity,"+ - "\"negativeNaN\":-NaN,"+ - "\"negativeFraction\":-.01,"+ - "\"tooManyZerosFraction\":00.001,"+ - "\"negativeHexFloat\":-0x1.fffp1,"+ - "\"hexFloat\":0x1.0P-1074,"+ - "\"floatIdentifier\":0.1f,"+ - "\"doubleIdentifier\":0.1d"+ - "}"; - JSONObject jsonObject = new JSONObject(str); - Object obj; - obj = jsonObject.get( "hexNumber" ); - assertFalse( "hexNumber must not be a number (should throw exception!?)", - obj instanceof Number ); - assertTrue("hexNumber currently evaluates to string", - obj.equals("-0x123")); - assertTrue( "tooManyZeros currently evaluates to string", - jsonObject.get( "tooManyZeros" ).equals("00")); - obj = jsonObject.get("negativeInfinite"); - assertTrue( "negativeInfinite currently evaluates to string", - obj.equals("-Infinity")); - obj = jsonObject.get("negativeNaN"); - assertTrue( "negativeNaN currently evaluates to string", - obj.equals("-NaN")); - assertTrue( "negativeFraction currently evaluates to double -0.01", - jsonObject.get( "negativeFraction" ).equals(new Double(-0.01))); - assertTrue( "tooManyZerosFraction currently evaluates to double 0.001", - jsonObject.get( "tooManyZerosFraction" ).equals(new Double(0.001))); - assertTrue( "negativeHexFloat currently evaluates to double -3.99951171875", - jsonObject.get( "negativeHexFloat" ).equals(new Double(-3.99951171875))); - assertTrue("hexFloat currently evaluates to double 4.9E-324", - jsonObject.get("hexFloat").equals(new Double(4.9E-324))); - assertTrue("floatIdentifier currently evaluates to double 0.1", - jsonObject.get("floatIdentifier").equals(new Double(0.1))); - assertTrue("doubleIdentifier currently evaluates to double 0.1", - jsonObject.get("doubleIdentifier").equals(new Double(0.1))); - } - - /** - * Tests how JSONObject get[type] handles incorrect types - */ - @Test - public void jsonObjectNonAndWrongValues() { - String str = - "{"+ - "\"trueKey\":true,"+ - "\"falseKey\":false,"+ - "\"trueStrKey\":\"true\","+ - "\"falseStrKey\":\"false\","+ - "\"stringKey\":\"hello world!\","+ - "\"intKey\":42,"+ - "\"intStrKey\":\"43\","+ - "\"longKey\":1234567890123456789,"+ - "\"longStrKey\":\"987654321098765432\","+ - "\"doubleKey\":-23.45e7,"+ - "\"doubleStrKey\":\"00001.000\","+ - "\"arrayKey\":[0,1,2],"+ - "\"objectKey\":{\"myKey\":\"myVal\"}"+ - "}"; - JSONObject jsonObject = new JSONObject(str); - try { - jsonObject.getBoolean("nonKey"); - assertTrue("Expected an exception", false); - } catch (JSONException e) { - assertTrue("expecting an exception message", - "JSONObject[\"nonKey\"] not found.".equals(e.getMessage())); - } - try { - jsonObject.getBoolean("stringKey"); - assertTrue("Expected an exception", false); - } catch (JSONException e) { - assertTrue("Expecting an exception message", - "JSONObject[\"stringKey\"] is not a Boolean.". - equals(e.getMessage())); - } - try { - jsonObject.getString("nonKey"); - assertTrue("Expected an exception", false); - } catch (JSONException e) { - assertTrue("Expecting an exception message", - "JSONObject[\"nonKey\"] not found.". - equals(e.getMessage())); - } - try { - jsonObject.getString("trueKey"); - assertTrue("Expected an exception", false); - } catch (JSONException e) { - assertTrue("Expecting an exception message", - "JSONObject[\"trueKey\"] not a string.". - equals(e.getMessage())); - } - try { - jsonObject.getDouble("nonKey"); - assertTrue("Expected an exception", false); - } catch (JSONException e) { - assertTrue("Expecting an exception message", - "JSONObject[\"nonKey\"] not found.". - equals(e.getMessage())); - } - try { - jsonObject.getDouble("stringKey"); - assertTrue("Expected an exception", false); - } catch (JSONException e) { - assertTrue("Expecting an exception message", - "JSONObject[\"stringKey\"] is not a number.". - equals(e.getMessage())); - } - try { - jsonObject.getInt("nonKey"); - assertTrue("Expected an exception", false); - } catch (JSONException e) { - assertTrue("Expecting an exception message", - "JSONObject[\"nonKey\"] not found.". - equals(e.getMessage())); - } - try { - jsonObject.getInt("stringKey"); - assertTrue("Expected an exception", false); - } catch (JSONException e) { - assertTrue("Expecting an exception message", - "JSONObject[\"stringKey\"] is not an int.". - equals(e.getMessage())); - } - try { - jsonObject.getLong("nonKey"); - assertTrue("Expected an exception", false); - } catch (JSONException e) { - assertTrue("Expecting an exception message", - "JSONObject[\"nonKey\"] not found.". - equals(e.getMessage())); - } - try { - jsonObject.getLong("stringKey"); - assertTrue("Expected an exception", false); - } catch (JSONException e) { - assertTrue("Expecting an exception message", - "JSONObject[\"stringKey\"] is not a long.". - equals(e.getMessage())); - } - try { - jsonObject.getJSONArray("nonKey"); - assertTrue("Expected an exception", false); - } catch (JSONException e) { - assertTrue("Expecting an exception message", - "JSONObject[\"nonKey\"] not found.". - equals(e.getMessage())); - } - try { - jsonObject.getJSONArray("stringKey"); - assertTrue("Expected an exception", false); - } catch (JSONException e) { - assertTrue("Expecting an exception message", - "JSONObject[\"stringKey\"] is not a JSONArray.". - equals(e.getMessage())); - } - try { - jsonObject.getJSONObject("nonKey"); - assertTrue("Expected an exception", false); - } catch (JSONException e) { - assertTrue("Expecting an exception message", - "JSONObject[\"nonKey\"] not found.". - equals(e.getMessage())); - } - try { - jsonObject.getJSONObject("stringKey"); - assertTrue("Expected an exception", false); - } catch (JSONException e) { - assertTrue("Expecting an exception message", - "JSONObject[\"stringKey\"] is not a JSONObject.". - equals(e.getMessage())); - } - } - - /** - * This test documents an unexpected numeric behavior. - * A double that ends with .0 is parsed, serialized, then - * parsed again. On the second parse, it has become an int. - */ - @Test - public void unexpectedDoubleToIntConversion() { - String key30 = "key30"; - String key31 = "key31"; - JSONObject jsonObject = new JSONObject(); - jsonObject.put(key30, new Double(3.0)); - jsonObject.put(key31, new Double(3.1)); - - assertTrue("3.0 should remain a double", - jsonObject.getDouble(key30) == 3); - assertTrue("3.1 should remain a double", - jsonObject.getDouble(key31) == 3.1); - - // turns 3.0 into 3. - String serializedString = jsonObject.toString(); - JSONObject deserialized = new JSONObject(serializedString); - assertTrue("3.0 is now an int", deserialized.get(key30) instanceof Integer); - assertTrue("3.0 can still be interpreted as a double", - deserialized.getDouble(key30) == 3.0); - assertTrue("3.1 remains a double", deserialized.getDouble(key31) == 3.1); - } - - /** - * Document behaviors of big numbers. Includes both JSONObject - * and JSONArray tests - */ - @Test - public void bigNumberOperations() { - /** - * JSONObject tries to parse BigInteger as a bean, but it only has - * one getter, getLowestBitSet(). The value is lost and an unhelpful - * value is stored. This should be fixed. - */ - BigInteger bigInteger = new BigInteger("123456789012345678901234567890"); - JSONObject jsonObject = new JSONObject(bigInteger); - Object obj = jsonObject.get("lowestSetBit"); - assertTrue("JSONObject only has 1 value", jsonObject.length() == 1); - assertTrue("JSONObject parses BigInteger as the Integer lowestBitSet", - obj instanceof Integer); - assertTrue("this bigInteger lowestBitSet happens to be 1", - obj.equals(1)); - - /** - * JSONObject tries to parse BigDecimal as a bean, but it has - * no getters, The value is lost and no value is stored. - * This should be fixed. - */ - BigDecimal bigDecimal = new BigDecimal( - "123456789012345678901234567890.12345678901234567890123456789"); - jsonObject = new JSONObject(bigDecimal); - assertTrue("large bigDecimal is not stored", jsonObject.length() == 0); - - /** - * JSONObject put(String, Object) method stores and serializes - * bigInt and bigDec correctly. Nothing needs to change. - */ - jsonObject = new JSONObject(); - jsonObject.put("bigInt", bigInteger); - assertTrue("jsonObject.put() handles bigInt correctly", - jsonObject.get("bigInt").equals(bigInteger)); - assertTrue("jsonObject.getBigInteger() handles bigInt correctly", - jsonObject.getBigInteger("bigInt").equals(bigInteger)); - assertTrue("jsonObject.optBigInteger() handles bigInt correctly", - jsonObject.optBigInteger("bigInt", BigInteger.ONE).equals(bigInteger)); - assertTrue("jsonObject serializes bigInt correctly", - jsonObject.toString().equals("{\"bigInt\":123456789012345678901234567890}")); - jsonObject = new JSONObject(); - jsonObject.put("bigDec", bigDecimal); - assertTrue("jsonObject.put() handles bigDec correctly", - jsonObject.get("bigDec").equals(bigDecimal)); - assertTrue("jsonObject.getBigDecimal() handles bigDec correctly", - jsonObject.getBigDecimal("bigDec").equals(bigDecimal)); - assertTrue("jsonObject.optBigDecimal() handles bigDec correctly", - jsonObject.optBigDecimal("bigDec", BigDecimal.ONE).equals(bigDecimal)); - assertTrue("jsonObject serializes bigDec correctly", - jsonObject.toString().equals( - "{\"bigDec\":123456789012345678901234567890.12345678901234567890123456789}")); - - /** - * exercise some exceptions - */ - try { - jsonObject.getBigDecimal("bigInt"); - assertTrue("expected an exeption", false); - } catch (JSONException ignored) {} - obj = jsonObject.optBigDecimal("bigInt", BigDecimal.ONE); - assertTrue("expected BigDecimal", obj.equals(BigDecimal.ONE)); - try { - jsonObject.getBigInteger("bigDec"); - assertTrue("expected an exeption", false); - } catch (JSONException ignored) {} - jsonObject.put("stringKey", "abc"); - try { - jsonObject.getBigDecimal("stringKey"); - assertTrue("expected an exeption", false); - } catch (JSONException ignored) {} - obj = jsonObject.optBigInteger("bigDec", BigInteger.ONE); - assertTrue("expected BigInteger", obj.equals(BigInteger.ONE)); - - /** - * JSONObject.numberToString() works correctly, nothing to change. - */ - String str = JSONObject.numberToString(bigInteger); - assertTrue("numberToString() handles bigInteger correctly", - str.equals("123456789012345678901234567890")); - str = JSONObject.numberToString(bigDecimal); - assertTrue("numberToString() handles bigDecimal correctly", - str.equals("123456789012345678901234567890.12345678901234567890123456789")); - - /** - * JSONObject.stringToValue() turns bigInt into an accurate string, - * and rounds bigDec. This incorrect, but users may have come to - * expect this behavior. Change would be marginally better, but - * might inconvenience users. - */ - obj = JSONObject.stringToValue(bigInteger.toString()); - assertTrue("stringToValue() turns bigInteger string into string", - obj instanceof String); - obj = JSONObject.stringToValue(bigDecimal.toString()); - assertTrue("stringToValue() changes bigDecimal string", - !obj.toString().equals(bigDecimal.toString())); - - /** - * wrap() vs put() big number behavior is now the same. - */ - // bigInt map ctor - Map map = new HashMap(); - map.put("bigInt", bigInteger); - jsonObject = new JSONObject(map); - String actualFromMapStr = jsonObject.toString(); - assertTrue("bigInt in map (or array or bean) is a string", - actualFromMapStr.equals( - "{\"bigInt\":123456789012345678901234567890}")); - // bigInt put - jsonObject = new JSONObject(); - jsonObject.put("bigInt", bigInteger); - String actualFromPutStr = jsonObject.toString(); - assertTrue("bigInt from put is a number", - actualFromPutStr.equals( - "{\"bigInt\":123456789012345678901234567890}")); - // bigDec map ctor - map = new HashMap(); - map.put("bigDec", bigDecimal); - jsonObject = new JSONObject(map); - actualFromMapStr = jsonObject.toString(); - assertTrue("bigDec in map (or array or bean) is a bigDec", - actualFromMapStr.equals( - "{\"bigDec\":123456789012345678901234567890.12345678901234567890123456789}")); - // bigDec put - jsonObject = new JSONObject(); - jsonObject.put("bigDec", bigDecimal); - actualFromPutStr = jsonObject.toString(); - assertTrue("bigDec from put is a number", - actualFromPutStr.equals( - "{\"bigDec\":123456789012345678901234567890.12345678901234567890123456789}")); - // bigInt,bigDec put - JSONArray jsonArray = new JSONArray(); - jsonArray.put(bigInteger); - jsonArray.put(bigDecimal); - actualFromPutStr = jsonArray.toString(); - assertTrue("bigInt, bigDec from put is a number", - actualFromPutStr.equals( - "[123456789012345678901234567890,123456789012345678901234567890.12345678901234567890123456789]")); - assertTrue("getBigInt is bigInt", jsonArray.getBigInteger(0).equals(bigInteger)); - assertTrue("getBigDec is bigDec", jsonArray.getBigDecimal(1).equals(bigDecimal)); - assertTrue("optBigInt is bigInt", jsonArray.optBigInteger(0, BigInteger.ONE).equals(bigInteger)); - assertTrue("optBigDec is bigDec", jsonArray.optBigDecimal(1, BigDecimal.ONE).equals(bigDecimal)); - jsonArray.put(Boolean.TRUE); - try { - jsonArray.getBigInteger(2); - assertTrue("should not be able to get big int", false); - } catch (Exception ignored) {} - try { - jsonArray.getBigDecimal(2); - assertTrue("should not be able to get big dec", false); - } catch (Exception ignored) {} - assertTrue("optBigInt is default", jsonArray.optBigInteger(2, BigInteger.ONE).equals(BigInteger.ONE)); - assertTrue("optBigDec is default", jsonArray.optBigDecimal(2, BigDecimal.ONE).equals(BigDecimal.ONE)); - - // bigInt,bigDec list ctor - List list = new ArrayList(); - list.add(bigInteger); - list.add(bigDecimal); - jsonArray = new JSONArray(list); - String actualFromListStr = jsonArray.toString(); - assertTrue("bigInt, bigDec in list is a bigInt, bigDec", - actualFromListStr.equals( - "[123456789012345678901234567890,123456789012345678901234567890.12345678901234567890123456789]")); - // bigInt bean ctor - MyBigNumberBean myBigNumberBean = mock(MyBigNumberBean.class); - when(myBigNumberBean.getBigInteger()).thenReturn(new BigInteger("123456789012345678901234567890")); - jsonObject = new JSONObject(myBigNumberBean); - String actualFromBeanStr = jsonObject.toString(); - // can't do a full string compare because mockery adds an extra key/value - assertTrue("bigInt from bean ctor is a bigInt", - actualFromBeanStr.contains("123456789012345678901234567890")); - // bigDec bean ctor - myBigNumberBean = mock(MyBigNumberBean.class); - when(myBigNumberBean.getBigDecimal()).thenReturn(new BigDecimal("123456789012345678901234567890.12345678901234567890123456789")); - jsonObject = new JSONObject(myBigNumberBean); - actualFromBeanStr = jsonObject.toString(); - // can't do a full string compare because mockery adds an extra key/value - assertTrue("bigDec from bean ctor is a bigDec", - actualFromBeanStr.contains("123456789012345678901234567890.12345678901234567890123456789")); - // bigInt,bigDec wrap() - obj = JSONObject.wrap(bigInteger); - assertTrue("wrap() returns big num",obj.equals(bigInteger)); - obj = JSONObject.wrap(bigDecimal); - assertTrue("wrap() returns string",obj.equals(bigDecimal)); - - } - - /** - * The purpose for the static method getNames() methods are not clear. - * This method is not called from within JSON-Java. Most likely - * uses are to prep names arrays for: - * JSONObject(JSONObject jo, String[] names) - * JSONObject(Object object, String names[]), - */ - @Test - public void jsonObjectNames() { - JSONObject jsonObject; - - // getNames() from null JSONObject - assertTrue("null names from null Object", - null == JSONObject.getNames((Object)null)); - - // getNames() from object with no fields - assertTrue("null names from Object with no fields", - null == JSONObject.getNames(new MyJsonString())); - - // getNames from new JSONOjbect - jsonObject = new JSONObject(); - String [] names = JSONObject.getNames(jsonObject); - assertTrue("names should be null", names == null); - - - // getNames() from empty JSONObject - String emptyStr = "{}"; - jsonObject = new JSONObject(emptyStr); - assertTrue("empty JSONObject should have null names", - null == JSONObject.getNames(jsonObject)); - - // getNames() from JSONObject - String str = - "{"+ - "\"trueKey\":true,"+ - "\"falseKey\":false,"+ - "\"stringKey\":\"hello world!\","+ - "}"; - jsonObject = new JSONObject(str); - names = JSONObject.getNames(jsonObject); - JSONArray jsonArray = new JSONArray(names); - - // validate JSON - Object doc = Configuration.defaultConfiguration().jsonProvider() - .parse(jsonArray.toString()); - List docList = JsonPath.read(doc, "$"); - assertTrue("expected 3 items", docList.size() == 3); - assertTrue( - "expected to find trueKey", - ((List) JsonPath.read(doc, "$[?(@=='trueKey')]")).size() == 1); - assertTrue( - "expected to find falseKey", - ((List) JsonPath.read(doc, "$[?(@=='falseKey')]")).size() == 1); - assertTrue( - "expected to find stringKey", - ((List) JsonPath.read(doc, "$[?(@=='stringKey')]")).size() == 1); - - /** - * getNames() from an enum with properties has an interesting result. - * It returns the enum values, not the selected enum properties - */ - MyEnumField myEnumField = MyEnumField.VAL1; - names = JSONObject.getNames(myEnumField); - - // validate JSON - jsonArray = new JSONArray(names); - doc = Configuration.defaultConfiguration().jsonProvider() - .parse(jsonArray.toString()); - docList = JsonPath.read(doc, "$"); - assertTrue("expected 3 items", docList.size() == 3); - assertTrue( - "expected to find VAL1", - ((List) JsonPath.read(doc, "$[?(@=='VAL1')]")).size() == 1); - assertTrue( - "expected to find VAL2", - ((List) JsonPath.read(doc, "$[?(@=='VAL2')]")).size() == 1); - assertTrue( - "expected to find VAL3", - ((List) JsonPath.read(doc, "$[?(@=='VAL3')]")).size() == 1); - - /** - * A bean is also an object. But in order to test the static - * method getNames(), this particular bean needs some public - * data members. - */ - MyPublicClass myPublicClass = new MyPublicClass(); - names = JSONObject.getNames(myPublicClass); - - // validate JSON - jsonArray = new JSONArray(names); - doc = Configuration.defaultConfiguration().jsonProvider() - .parse(jsonArray.toString()); - docList = JsonPath.read(doc, "$"); - assertTrue("expected 2 items", docList.size() == 2); - assertTrue( - "expected to find publicString", - ((List) JsonPath.read(doc, "$[?(@=='publicString')]")).size() == 1); - assertTrue( - "expected to find publicInt", - ((List) JsonPath.read(doc, "$[?(@=='publicInt')]")).size() == 1); - } - - /** - * Populate a JSONArray from an empty JSONObject names() method. - * It should be empty. - */ - @Test - public void emptyJsonObjectNamesToJsonAray() { - JSONObject jsonObject = new JSONObject(); - JSONArray jsonArray = jsonObject.names(); - assertTrue("jsonArray should be null", jsonArray == null); - } - - /** - * Populate a JSONArray from a JSONObject names() method. - * Confirm that it contains the expected names. - */ - @Test - public void jsonObjectNamesToJsonAray() { - String str = - "{"+ - "\"trueKey\":true,"+ - "\"falseKey\":false,"+ - "\"stringKey\":\"hello world!\","+ - "}"; - - JSONObject jsonObject = new JSONObject(str); - JSONArray jsonArray = jsonObject.names(); - - // validate JSON - Object doc = Configuration.defaultConfiguration().jsonProvider().parse(jsonArray.toString()); - assertTrue("expected 3 top level items", ((List)(JsonPath.read(doc, "$"))).size() == 3); - assertTrue("expected to find trueKey", ((List) JsonPath.read(doc, "$[?(@=='trueKey')]")).size() == 1); - assertTrue("expected to find falseKey", ((List) JsonPath.read(doc, "$[?(@=='falseKey')]")).size() == 1); - assertTrue("expected to find stringKey", ((List) JsonPath.read(doc, "$[?(@=='stringKey')]")).size() == 1); - } - - /** - * Exercise the JSONObject increment() method. - */ - @Test - public void jsonObjectIncrement() { - String str = - "{"+ - "\"keyLong\":9999999991,"+ - "\"keyDouble\":1.1"+ - "}"; - JSONObject jsonObject = new JSONObject(str); - jsonObject.increment("keyInt"); - jsonObject.increment("keyInt"); - jsonObject.increment("keyLong"); - jsonObject.increment("keyDouble"); - jsonObject.increment("keyInt"); - jsonObject.increment("keyLong"); - jsonObject.increment("keyDouble"); - /** - * JSONObject constructor won't handle these types correctly, but - * adding them via put works. - */ - jsonObject.put("keyFloat", new Float(1.1)); - jsonObject.put("keyBigInt", new BigInteger("123456789123456789123456789123456780")); - jsonObject.put("keyBigDec", new BigDecimal("123456789123456789123456789123456780.1")); - jsonObject.increment("keyFloat"); - jsonObject.increment("keyFloat"); - jsonObject.increment("keyBigInt"); - jsonObject.increment("keyBigDec"); - - // validate JSON - Object doc = Configuration.defaultConfiguration().jsonProvider().parse(jsonObject.toString()); - assertTrue("expected 6 top level items", ((Map)(JsonPath.read(doc, "$"))).size() == 6); - assertTrue("expected 3", Integer.valueOf(3).equals(jsonObject.query("/keyInt"))); - assertTrue("expected 9999999993", Long.valueOf(9999999993L).equals(jsonObject.query("/keyLong"))); - assertTrue("expected 3.1", Double.valueOf(3.1).equals(jsonObject.query("/keyDouble"))); - assertTrue("expected 123456789123456789123456789123456781", new BigInteger("123456789123456789123456789123456781").equals(jsonObject.query("/keyBigInt"))); - assertTrue("expected 123456789123456789123456789123456781.1", new BigDecimal("123456789123456789123456789123456781.1").equals(jsonObject.query("/keyBigDec"))); - - /** - * Should work the same way on any platform! @see https://docs.oracle - * .com/javase/specs/jls/se7/html/jls-4.html#jls-4.2.3 This is the - * effect of a float to double conversion and is inherent to the - * shortcomings of the IEEE 754 format, when converting 32-bit into - * double-precision 64-bit. Java type-casts float to double. A 32 bit - * float is type-casted to 64 bit double by simply appending zero-bits - * to the mantissa (and extended the signed exponent by 3 bits.) and - * there is no way to obtain more information than it is stored in the - * 32-bits float. - * - * Like 1/3 cannot be represented as base10 number because it is - * periodically, 1/5 (for example) cannot be represented as base2 number - * since it is periodically in base2 (take a look at - * http://www.h-schmidt.net/FloatConverter/) The same happens to 3.1, - * that decimal number (base10 representation) is periodic in base2 - * representation, therefore appending zero-bits is inaccurate. Only - * repeating the periodically occuring bits (0110) would be a proper - * conversion. However one cannot detect from a 32 bit IEE754 - * representation which bits would "repeat infinitely", since the - * missing bits would not fit into the 32 bit float, i.e. the - * information needed simply is not there! - */ - assertTrue("expected 3.0999999046325684", Double.valueOf(3.0999999046325684).equals(jsonObject.query("/keyFloat"))); - - /** - * float f = 3.1f; double df = (double) f; double d = 3.1d; - * System.out.println - * (Integer.toBinaryString(Float.floatToRawIntBits(f))); - * System.out.println - * (Long.toBinaryString(Double.doubleToRawLongBits(df))); - * System.out.println - * (Long.toBinaryString(Double.doubleToRawLongBits(d))); - * - * - Float: - * seeeeeeeemmmmmmmmmmmmmmmmmmmmmmm - * 1000000010001100110011001100110 - * - Double - * seeeeeeeeeeemmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm - * 10000000 10001100110011001100110 - * 100000000001000110011001100110011000000000000000000000000000000 - * 100000000001000110011001100110011001100110011001100110011001101 - */ - - /** - * Examples of well documented but probably unexpected behavior in - * java / with 32-bit float to 64-bit float conversion. - */ - assertFalse("Document unexpected behaviour with explicit type-casting float as double!", (double)0.2f == 0.2d ); - assertFalse("Document unexpected behaviour with implicit type-cast!", 0.2f == 0.2d ); - Double d1 = new Double( 1.1f ); - Double d2 = new Double( "1.1f" ); - assertFalse( "Document implicit type cast from float to double before calling Double(double d) constructor", d1.equals( d2 ) ); - - assertTrue( "Correctly converting float to double via base10 (string) representation!", new Double( 3.1d ).equals( new Double( new Float( 3.1f ).toString() ) ) ); - - // Pinpointing the not so obvious "buggy" conversion from float to double in JSONObject - JSONObject jo = new JSONObject(); - jo.put( "bug", 3.1f ); // will call put( String key, double value ) with implicit and "buggy" type-cast from float to double - assertFalse( "The java-compiler did add some zero bits for you to the mantissa (unexpected, but well documented)", jo.get( "bug" ).equals( new Double( 3.1d ) ) ); - - JSONObject inc = new JSONObject(); - inc.put( "bug", new Float( 3.1f ) ); // This will put in instance of Float into JSONObject, i.e. call put( String key, Object value ) - assertTrue( "Everything is ok here!", inc.get( "bug" ) instanceof Float ); - inc.increment( "bug" ); // after adding 1, increment will call put( String key, double value ) with implicit and "buggy" type-cast from float to double! - // this.put(key, (Float) value + 1); - // 1. The (Object)value will be typecasted to (Float)value since it is an instanceof Float actually nothing is done. - // 2. Float instance will be autoboxed into float because the + operator will work on primitives not Objects! - // 3. A float+float operation will be performed and results into a float primitive. - // 4. There is no method that matches the signature put( String key, float value), java-compiler will choose the method - // put( String key, double value) and does an implicit type-cast(!) by appending zero-bits to the mantissa - assertTrue( "JSONObject increment converts Float to Double", jo.get( "bug" ) instanceof Double ); - // correct implementation (with change of behavior) would be: - // this.put(key, new Float((Float) value + 1)); - // Probably it would be better to deprecate the method and remove some day, while convenient processing the "payload" is not - // really in the the scope of a JSON-library (IMHO.) - - } - - /** - * Exercise JSONObject numberToString() method - */ - @Test - public void jsonObjectNumberToString() { - String str; - Double dVal; - Integer iVal = 1; - str = JSONObject.numberToString(iVal); - assertTrue("expected "+iVal+" actual "+str, iVal.toString().equals(str)); - dVal = 12.34; - str = JSONObject.numberToString(dVal); - assertTrue("expected "+dVal+" actual "+str, dVal.toString().equals(str)); - dVal = 12.34e27; - str = JSONObject.numberToString(dVal); - assertTrue("expected "+dVal+" actual "+str, dVal.toString().equals(str)); - // trailing .0 is truncated, so it doesn't quite match toString() - dVal = 5000000.0000000; - str = JSONObject.numberToString(dVal); - assertTrue("expected 5000000 actual "+str, str.equals("5000000")); - } - - /** - * Exercise JSONObject put() and similar() methods - */ - @Test - public void jsonObjectPut() { - String expectedStr = - "{"+ - "\"trueKey\":true,"+ - "\"falseKey\":false,"+ - "\"arrayKey\":[0,1,2],"+ - "\"objectKey\":{"+ - "\"myKey1\":\"myVal1\","+ - "\"myKey2\":\"myVal2\","+ - "\"myKey3\":\"myVal3\","+ - "\"myKey4\":\"myVal4\""+ - "}"+ - "}"; - JSONObject jsonObject = new JSONObject(); - jsonObject.put("trueKey", true); - jsonObject.put("falseKey", false); - Integer [] intArray = { 0, 1, 2 }; - jsonObject.put("arrayKey", Arrays.asList(intArray)); - Map myMap = new HashMap(); - myMap.put("myKey1", "myVal1"); - myMap.put("myKey2", "myVal2"); - myMap.put("myKey3", "myVal3"); - myMap.put("myKey4", "myVal4"); - jsonObject.put("objectKey", myMap); - - // validate JSON - Object doc = Configuration.defaultConfiguration().jsonProvider().parse(jsonObject.toString()); - assertTrue("expected 4 top level items", ((Map)(JsonPath.read(doc, "$"))).size() == 4); - assertTrue("expected true", Boolean.TRUE.equals(jsonObject.query("/trueKey"))); - assertTrue("expected false", Boolean.FALSE.equals(jsonObject.query("/falseKey"))); - assertTrue("expected 3 arrayKey items", ((List)(JsonPath.read(doc, "$.arrayKey"))).size() == 3); - assertTrue("expected 0", Integer.valueOf(0).equals(jsonObject.query("/arrayKey/0"))); - assertTrue("expected 1", Integer.valueOf(1).equals(jsonObject.query("/arrayKey/1"))); - assertTrue("expected 2", Integer.valueOf(2).equals(jsonObject.query("/arrayKey/2"))); - assertTrue("expected 4 objectKey items", ((Map)(JsonPath.read(doc, "$.objectKey"))).size() == 4); - assertTrue("expected myVal1", "myVal1".equals(jsonObject.query("/objectKey/myKey1"))); - assertTrue("expected myVal2", "myVal2".equals(jsonObject.query("/objectKey/myKey2"))); - assertTrue("expected myVal3", "myVal3".equals(jsonObject.query("/objectKey/myKey3"))); - assertTrue("expected myVal4", "myVal4".equals(jsonObject.query("/objectKey/myKey4"))); - - jsonObject.remove("trueKey"); - JSONObject expectedJsonObject = new JSONObject(expectedStr); - assertTrue("unequal jsonObjects should not be similar", - !jsonObject.similar(expectedJsonObject)); - assertTrue("jsonObject should not be similar to jsonArray", - !jsonObject.similar(new JSONArray())); - - String aCompareValueStr = "{\"a\":\"aval\",\"b\":true}"; - String bCompareValueStr = "{\"a\":\"notAval\",\"b\":true}"; - JSONObject aCompareValueJsonObject = new JSONObject(aCompareValueStr); - JSONObject bCompareValueJsonObject = new JSONObject(bCompareValueStr); - assertTrue("different values should not be similar", - !aCompareValueJsonObject.similar(bCompareValueJsonObject)); - - String aCompareObjectStr = "{\"a\":\"aval\",\"b\":{}}"; - String bCompareObjectStr = "{\"a\":\"aval\",\"b\":true}"; - JSONObject aCompareObjectJsonObject = new JSONObject(aCompareObjectStr); - JSONObject bCompareObjectJsonObject = new JSONObject(bCompareObjectStr); - assertTrue("different nested JSONObjects should not be similar", - !aCompareObjectJsonObject.similar(bCompareObjectJsonObject)); - - String aCompareArrayStr = "{\"a\":\"aval\",\"b\":[]}"; - String bCompareArrayStr = "{\"a\":\"aval\",\"b\":true}"; - JSONObject aCompareArrayJsonObject = new JSONObject(aCompareArrayStr); - JSONObject bCompareArrayJsonObject = new JSONObject(bCompareArrayStr); - assertTrue("different nested JSONArrays should not be similar", - !aCompareArrayJsonObject.similar(bCompareArrayJsonObject)); - } - - /** - * Exercise JSONObject toString() method - */ - @Test - public void jsonObjectToString() { - String str = - "{"+ - "\"trueKey\":true,"+ - "\"falseKey\":false,"+ - "\"arrayKey\":[0,1,2],"+ - "\"objectKey\":{"+ - "\"myKey1\":\"myVal1\","+ - "\"myKey2\":\"myVal2\","+ - "\"myKey3\":\"myVal3\","+ - "\"myKey4\":\"myVal4\""+ - "}"+ - "}"; - JSONObject jsonObject = new JSONObject(str); - - // validate JSON - Object doc = Configuration.defaultConfiguration().jsonProvider().parse(jsonObject.toString()); - assertTrue("expected 4 top level items", ((Map)(JsonPath.read(doc, "$"))).size() == 4); - assertTrue("expected true", Boolean.TRUE.equals(jsonObject.query("/trueKey"))); - assertTrue("expected false", Boolean.FALSE.equals(jsonObject.query("/falseKey"))); - assertTrue("expected 3 arrayKey items", ((List)(JsonPath.read(doc, "$.arrayKey"))).size() == 3); - assertTrue("expected 0", Integer.valueOf(0).equals(jsonObject.query("/arrayKey/0"))); - assertTrue("expected 1", Integer.valueOf(1).equals(jsonObject.query("/arrayKey/1"))); - assertTrue("expected 2", Integer.valueOf(2).equals(jsonObject.query("/arrayKey/2"))); - assertTrue("expected 4 objectKey items", ((Map)(JsonPath.read(doc, "$.objectKey"))).size() == 4); - assertTrue("expected myVal1", "myVal1".equals(jsonObject.query("/objectKey/myKey1"))); - assertTrue("expected myVal2", "myVal2".equals(jsonObject.query("/objectKey/myKey2"))); - assertTrue("expected myVal3", "myVal3".equals(jsonObject.query("/objectKey/myKey3"))); - assertTrue("expected myVal4", "myVal4".equals(jsonObject.query("/objectKey/myKey4"))); - } - - /** - * Exercise JSONObject toString() method with various indent levels. - */ - @Test - public void jsonObjectToStringIndent() { - String jsonObject0Str = - "{"+ - "\"key1\":" + - "[1,2," + - "{\"key3\":true}" + - "],"+ - "\"key2\":" + - "{\"key1\":\"val1\",\"key2\":" + - "{\"key2\":\"val2\"}" + - "},"+ - "\"key3\":" + - "[" + - "[1,2.1]" + - "," + - "[null]" + - "]"+ - "}"; - - String jsonObject1Str = - "{\n" + - " \"key1\": [\n" + - " 1,\n" + - " 2,\n" + - " {\"key3\": true}\n" + - " ],\n" + - " \"key2\": {\n" + - " \"key1\": \"val1\",\n" + - " \"key2\": {\"key2\": \"val2\"}\n" + - " },\n" + - " \"key3\": [\n" + - " [\n" + - " 1,\n" + - " 2.1\n" + - " ],\n" + - " [null]\n" + - " ]\n" + - "}"; - String jsonObject4Str = - "{\n" + - " \"key1\": [\n" + - " 1,\n" + - " 2,\n" + - " {\"key3\": true}\n" + - " ],\n" + - " \"key2\": {\n" + - " \"key1\": \"val1\",\n" + - " \"key2\": {\"key2\": \"val2\"}\n" + - " },\n" + - " \"key3\": [\n" + - " [\n" + - " 1,\n" + - " 2.1\n" + - " ],\n" + - " [null]\n" + - " ]\n" + - "}"; - JSONObject jsonObject = new JSONObject(jsonObject0Str); - assertEquals(jsonObject0Str, jsonObject.toString()); - assertEquals(jsonObject0Str, jsonObject.toString(0)); - assertEquals(jsonObject1Str, jsonObject.toString(1)); - assertEquals(jsonObject4Str, jsonObject.toString(4)); - } - - /** - * Explores how JSONObject handles maps. Insert a string/string map - * as a value in a JSONObject. It will remain a map. Convert the - * JSONObject to string, then create a new JSONObject from the string. - * In the new JSONObject, the value will be stored as a nested JSONObject. - * Confirm that map and nested JSONObject have the same contents. - */ - @Test - public void jsonObjectToStringSuppressWarningOnCastToMap() { - JSONObject jsonObject = new JSONObject(); - Map map = new HashMap<>(); - map.put("abc", "def"); - jsonObject.put("key", map); - - // validate JSON - Object doc = Configuration.defaultConfiguration().jsonProvider().parse(jsonObject.toString()); - assertTrue("expected 1 top level item", ((Map)(JsonPath.read(doc, "$"))).size() == 1); - assertTrue("expected 1 key item", ((Map)(JsonPath.read(doc, "$.key"))).size() == 1); - assertTrue("expected def", "def".equals(jsonObject.query("/key/abc"))); - } - - /** - * Explores how JSONObject handles collections. Insert a string collection - * as a value in a JSONObject. It will remain a collection. Convert the - * JSONObject to string, then create a new JSONObject from the string. - * In the new JSONObject, the value will be stored as a nested JSONArray. - * Confirm that collection and nested JSONArray have the same contents. - */ - @Test - public void jsonObjectToStringSuppressWarningOnCastToCollection() { - JSONObject jsonObject = new JSONObject(); - Collection collection = new ArrayList(); - collection.add("abc"); - // ArrayList will be added as an object - jsonObject.put("key", collection); - - // validate JSON - Object doc = Configuration.defaultConfiguration().jsonProvider().parse(jsonObject.toString()); - assertTrue("expected 1 top level item", ((Map)(JsonPath.read(doc, "$"))).size() == 1); - assertTrue("expected 1 key item", ((List)(JsonPath.read(doc, "$.key"))).size() == 1); - assertTrue("expected abc", "abc".equals(jsonObject.query("/key/0"))); - } - - /** - * Exercises the JSONObject.valueToString() method for various types - */ - @Test - public void valueToString() { - - assertTrue("null valueToString() incorrect", - "null".equals(JSONObject.valueToString(null))); - MyJsonString jsonString = new MyJsonString(); - assertTrue("jsonstring valueToString() incorrect", - "my string".equals(JSONObject.valueToString(jsonString))); - assertTrue("boolean valueToString() incorrect", - "true".equals(JSONObject.valueToString(Boolean.TRUE))); - assertTrue("non-numeric double", - "null".equals(JSONObject.doubleToString(Double.POSITIVE_INFINITY))); - String jsonObjectStr = - "{"+ - "\"key1\":\"val1\","+ - "\"key2\":\"val2\","+ - "\"key3\":\"val3\""+ - "}"; - JSONObject jsonObject = new JSONObject(jsonObjectStr); - assertTrue("jsonObject valueToString() incorrect", - JSONObject.valueToString(jsonObject).equals(jsonObject.toString())); - String jsonArrayStr = - "[1,2,3]"; - JSONArray jsonArray = new JSONArray(jsonArrayStr); - assertTrue("jsonArray valueToString() incorrect", - JSONObject.valueToString(jsonArray).equals(jsonArray.toString())); - Map map = new HashMap(); - map.put("key1", "val1"); - map.put("key2", "val2"); - map.put("key3", "val3"); - assertTrue("map valueToString() incorrect", - jsonObject.toString().equals(JSONObject.valueToString(map))); - Collection collection = new ArrayList(); - collection.add(new Integer(1)); - collection.add(new Integer(2)); - collection.add(new Integer(3)); - assertTrue("collection valueToString() expected: "+ - jsonArray.toString()+ " actual: "+ - JSONObject.valueToString(collection), - jsonArray.toString().equals(JSONObject.valueToString(collection))); - Integer[] array = { new Integer(1), new Integer(2), new Integer(3) }; - assertTrue("array valueToString() incorrect", - jsonArray.toString().equals(JSONObject.valueToString(array))); - } - - /** - * Confirm that https://github.com/douglascrockford/JSON-java/issues/167 is fixed. - * The following code was throwing a ClassCastException in the - * JSONObject(Map) constructor - */ - @Test - public void valueToStringConfirmException() { - Map myMap = new HashMap(); - myMap.put(1, "myValue"); - // this is the test, it should not throw an exception - String str = JSONObject.valueToString(myMap); - // confirm result, just in case - Object doc = Configuration.defaultConfiguration().jsonProvider().parse(str); - assertTrue("expected 1 top level item", ((Map)(JsonPath.read(doc, "$"))).size() == 1); - assertTrue("expected myValue", "myValue".equals(JsonPath.read(doc, "$.1"))); - } - - /** - * Exercise the JSONObject wrap() method. Sometimes wrap() will change - * the object being wrapped, other times not. The purpose of wrap() is - * to ensure the value is packaged in a way that is compatible with how - * a JSONObject value or JSONArray value is supposed to be stored. - */ - @Test - public void wrapObject() { - // wrap(null) returns NULL - assertTrue("null wrap() incorrect", - JSONObject.NULL == JSONObject.wrap(null)); - - // wrap(Integer) returns Integer - Integer in = new Integer(1); - assertTrue("Integer wrap() incorrect", - in == JSONObject.wrap(in)); - - /** - * This test is to document the preferred behavior if BigDecimal is - * supported. Previously bd returned as a string, since it - * is recognized as being a Java package class. Now with explicit - * support for big numbers, it remains a BigDecimal - */ - Object bdWrap = JSONObject.wrap(BigDecimal.ONE); - assertTrue("BigDecimal.ONE evaluates to ONE", - bdWrap.equals(BigDecimal.ONE)); - - // wrap JSONObject returns JSONObject - String jsonObjectStr = - "{"+ - "\"key1\":\"val1\","+ - "\"key2\":\"val2\","+ - "\"key3\":\"val3\""+ - "}"; - JSONObject jsonObject = new JSONObject(jsonObjectStr); - assertTrue("JSONObject wrap() incorrect", - jsonObject == JSONObject.wrap(jsonObject)); - - // wrap collection returns JSONArray - Collection collection = new ArrayList(); - collection.add(new Integer(1)); - collection.add(new Integer(2)); - collection.add(new Integer(3)); - JSONArray jsonArray = (JSONArray) (JSONObject.wrap(collection)); - - // validate JSON - Object doc = Configuration.defaultConfiguration().jsonProvider().parse(jsonArray.toString()); - assertTrue("expected 3 top level items", ((List)(JsonPath.read(doc, "$"))).size() == 3); - assertTrue("expected 1", Integer.valueOf(1).equals(jsonArray.query("/0"))); - assertTrue("expected 2", Integer.valueOf(2).equals(jsonArray.query("/1"))); - assertTrue("expected 3", Integer.valueOf(3).equals(jsonArray.query("/2"))); - - // wrap Array returns JSONArray - Integer[] array = { new Integer(1), new Integer(2), new Integer(3) }; - JSONArray integerArrayJsonArray = (JSONArray)(JSONObject.wrap(array)); - - // validate JSON - doc = Configuration.defaultConfiguration().jsonProvider().parse(jsonArray.toString()); - assertTrue("expected 3 top level items", ((List)(JsonPath.read(doc, "$"))).size() == 3); - assertTrue("expected 1", Integer.valueOf(1).equals(jsonArray.query("/0"))); - assertTrue("expected 2", Integer.valueOf(2).equals(jsonArray.query("/1"))); - assertTrue("expected 3", Integer.valueOf(3).equals(jsonArray.query("/2"))); - - // validate JSON - doc = Configuration.defaultConfiguration().jsonProvider().parse(integerArrayJsonArray.toString()); - assertTrue("expected 3 top level items", ((List)(JsonPath.read(doc, "$"))).size() == 3); - assertTrue("expected 1", Integer.valueOf(1).equals(jsonArray.query("/0"))); - assertTrue("expected 2", Integer.valueOf(2).equals(jsonArray.query("/1"))); - assertTrue("expected 3", Integer.valueOf(3).equals(jsonArray.query("/2"))); - - // wrap map returns JSONObject - Map map = new HashMap(); - map.put("key1", "val1"); - map.put("key2", "val2"); - map.put("key3", "val3"); - JSONObject mapJsonObject = (JSONObject) (JSONObject.wrap(map)); - - // validate JSON - doc = Configuration.defaultConfiguration().jsonProvider().parse(mapJsonObject.toString()); - assertTrue("expected 3 top level items", ((Map)(JsonPath.read(doc, "$"))).size() == 3); - assertTrue("expected val1", "val1".equals(mapJsonObject.query("/key1"))); - assertTrue("expected val2", "val2".equals(mapJsonObject.query("/key2"))); - assertTrue("expected val3", "val3".equals(mapJsonObject.query("/key3"))); - } - - - /** - * RFC 7159 defines control characters to be U+0000 through U+001F. This test verifies that the parser is checking for these in expected ways. - */ - @Test - public void jsonObjectParseControlCharacters(){ - for(int i = 0;i<=0x001f;i++){ - final String charString = String.valueOf((char)i); - final String source = "{\"key\":\""+charString+"\"}"; - try { - JSONObject jo = new JSONObject(source); - assertTrue("Expected "+charString+"("+i+") in the JSON Object but did not find it.",charString.equals(jo.getString("key"))); - } catch (JSONException ex) { - assertTrue("Only \\0 (U+0000), \\n (U+000A), and \\r (U+000D) should cause an error. Instead "+charString+"("+i+") caused an error", - i=='\0' || i=='\n' || i=='\r' - ); - } - } - } - - /** - * Explore how JSONObject handles parsing errors. - */ - @Test - public void jsonObjectParsingErrors() { - try { - // does not start with '{' - String str = "abc"; - new JSONObject(str); - assertTrue("Expected an exception", false); - } catch (JSONException e) { - assertTrue("Expecting an exception message", - "A JSONObject text must begin with '{' at 1 [character 2 line 1]". - equals(e.getMessage())); - } - try { - // does not end with '}' - String str = "{"; - new JSONObject(str); - assertTrue("Expected an exception", false); - } catch (JSONException e) { - assertTrue("Expecting an exception message", - "A JSONObject text must end with '}' at 2 [character 3 line 1]". - equals(e.getMessage())); - } - try { - // key with no ':' - String str = "{\"myKey\" = true}"; - new JSONObject(str); - assertTrue("Expected an exception", false); - } catch (JSONException e) { - assertTrue("Expecting an exception message", - "Expected a ':' after a key at 10 [character 11 line 1]". - equals(e.getMessage())); - } - try { - // entries with no ',' separator - String str = "{\"myKey\":true \"myOtherKey\":false}"; - new JSONObject(str); - assertTrue("Expected an exception", false); - } catch (JSONException e) { - assertTrue("Expecting an exception message", - "Expected a ',' or '}' at 15 [character 16 line 1]". - equals(e.getMessage())); - } - try { - // append to wrong key - String str = "{\"myKey\":true, \"myOtherKey\":false}"; - JSONObject jsonObject = new JSONObject(str); - jsonObject.append("myKey", "hello"); - assertTrue("Expected an exception", false); - } catch (JSONException e) { - assertTrue("Expecting an exception message", - "JSONObject[myKey] is not a JSONArray.". - equals(e.getMessage())); - } - try { - // increment wrong key - String str = "{\"myKey\":true, \"myOtherKey\":false}"; - JSONObject jsonObject = new JSONObject(str); - jsonObject.increment("myKey"); - assertTrue("Expected an exception", false); - } catch (JSONException e) { - assertTrue("Expecting an exception message", - "Unable to increment [\"myKey\"].". - equals(e.getMessage())); - } - try { - // invalid key - String str = "{\"myKey\":true, \"myOtherKey\":false}"; - JSONObject jsonObject = new JSONObject(str); - jsonObject.get(null); - assertTrue("Expected an exception", false); - } catch (JSONException e) { - assertTrue("Expecting an exception message", - "Null key.". - equals(e.getMessage())); - } - try { - // invalid numberToString() - JSONObject.numberToString((Number)null); - assertTrue("Expected an exception", false); - } catch (JSONException e) { - assertTrue("Expecting an exception message", - "Null pointer". - equals(e.getMessage())); - } - try { - // null put key - JSONObject jsonObject = new JSONObject("{}"); - jsonObject.put(null, 0); - assertTrue("Expected an exception", false); - } catch (NullPointerException ignored) { - } - try { - // multiple putOnce key - JSONObject jsonObject = new JSONObject("{}"); - jsonObject.putOnce("hello", "world"); - jsonObject.putOnce("hello", "world!"); - assertTrue("Expected an exception", false); - } catch (JSONException e) { - assertTrue("", true); - } - try { - // test validity of invalid double - JSONObject.testValidity(Double.NaN); - assertTrue("Expected an exception", false); - } catch (JSONException e) { - assertTrue("", true); - } - try { - // test validity of invalid float - JSONObject.testValidity(Float.NEGATIVE_INFINITY); - assertTrue("Expected an exception", false); - } catch (JSONException e) { - assertTrue("", true); - } - } - - /** - * Confirm behavior when putOnce() is called with null parameters - */ - @Test - public void jsonObjectPutOnceNull() { - JSONObject jsonObject = new JSONObject(); - jsonObject.putOnce(null, null); - assertTrue("jsonObject should be empty", jsonObject.length() == 0); - } - - /** - * Exercise JSONObject opt(key, default) method. - */ - @Test - public void jsonObjectOptDefault() { - - String str = "{\"myKey\": \"myval\", \"hiKey\": null}"; - JSONObject jsonObject = new JSONObject(str); - - assertTrue("optBigDecimal() should return default BigDecimal", - BigDecimal.TEN.compareTo(jsonObject.optBigDecimal("myKey", BigDecimal.TEN))==0); - assertTrue("optBigInteger() should return default BigInteger", - BigInteger.TEN.compareTo(jsonObject.optBigInteger("myKey",BigInteger.TEN ))==0); - assertTrue("optBoolean() should return default boolean", - jsonObject.optBoolean("myKey", true)); - assertTrue("optInt() should return default int", - 42 == jsonObject.optInt("myKey", 42)); - assertTrue("optEnum() should return default Enum", - MyEnum.VAL1.equals(jsonObject.optEnum(MyEnum.class, "myKey", MyEnum.VAL1))); - assertTrue("optJSONArray() should return null ", - null==jsonObject.optJSONArray("myKey")); - assertTrue("optJSONObject() should return null ", - null==jsonObject.optJSONObject("myKey")); - assertTrue("optLong() should return default long", - 42 == jsonObject.optLong("myKey", 42)); - assertTrue("optDouble() should return default double", - 42.3 == jsonObject.optDouble("myKey", 42.3)); - assertTrue("optString() should return default string", - "hi".equals(jsonObject.optString("hiKey", "hi"))); - } - - /** - * Exercise JSONObject opt(key, default) method when the key doesn't exist. - */ - @Test - public void jsonObjectOptNoKey() { - - JSONObject jsonObject = new JSONObject(); - - assertTrue("optBigDecimal() should return default BigDecimal", - BigDecimal.TEN.compareTo(jsonObject.optBigDecimal("myKey", BigDecimal.TEN))==0); - assertTrue("optBigInteger() should return default BigInteger", - BigInteger.TEN.compareTo(jsonObject.optBigInteger("myKey",BigInteger.TEN ))==0); - assertTrue("optBoolean() should return default boolean", - jsonObject.optBoolean("myKey", true)); - assertTrue("optInt() should return default int", - 42 == jsonObject.optInt("myKey", 42)); - assertTrue("optEnum() should return default Enum", - MyEnum.VAL1.equals(jsonObject.optEnum(MyEnum.class, "myKey", MyEnum.VAL1))); - assertTrue("optJSONArray() should return null ", - null==jsonObject.optJSONArray("myKey")); - assertTrue("optJSONObject() should return null ", - null==jsonObject.optJSONObject("myKey")); - assertTrue("optLong() should return default long", - 42 == jsonObject.optLong("myKey", 42)); - assertTrue("optDouble() should return default double", - 42.3 == jsonObject.optDouble("myKey", 42.3)); - assertTrue("optString() should return default string", - "hi".equals(jsonObject.optString("hiKey", "hi"))); - } - - /** - * Verifies that the opt methods properly convert string values. - */ - @Test - public void jsonObjectOptStringConversion() { - JSONObject jo = new JSONObject("{\"int\":\"123\",\"true\":\"true\",\"false\":\"false\"}"); - assertTrue("unexpected optBoolean value",jo.optBoolean("true",false)==true); - assertTrue("unexpected optBoolean value",jo.optBoolean("false",true)==false); - assertTrue("unexpected optInt value",jo.optInt("int",0)==123); - assertTrue("unexpected optLong value",jo.optLong("int",0)==123); - assertTrue("unexpected optDouble value",jo.optDouble("int",0.0)==123.0); - assertTrue("unexpected optBigInteger value",jo.optBigInteger("int",BigInteger.ZERO).compareTo(new BigInteger("123"))==0); - assertTrue("unexpected optBigDecimal value",jo.optBigDecimal("int",BigDecimal.ZERO).compareTo(new BigDecimal("123"))==0); - - } - - /** - * Confirm behavior when JSONObject put(key, null object) is called - */ - @Test - public void jsonObjectputNull() { - - // put null should remove the item. - String str = "{\"myKey\": \"myval\"}"; - JSONObject jsonObjectRemove = new JSONObject(str); - jsonObjectRemove.remove("myKey"); - - JSONObject jsonObjectPutNull = new JSONObject(str); - jsonObjectPutNull.put("myKey", (Object) null); - - // validate JSON - assertTrue("jsonObject should be empty", jsonObjectRemove.length() == 0 - && jsonObjectPutNull.length() == 0); - } - - /** - * Exercise JSONObject quote() method - * This purpose of quote() is to ensure that for strings with embedded - * quotes, the quotes are properly escaped. - */ - @Test - public void jsonObjectQuote() { - String str; - str = ""; - String quotedStr; - quotedStr = JSONObject.quote(str); - assertTrue("quote() expected escaped quotes, found "+quotedStr, - "\"\"".equals(quotedStr)); - str = "\"\""; - quotedStr = JSONObject.quote(str); - assertTrue("quote() expected escaped quotes, found "+quotedStr, - "\"\\\"\\\"\"".equals(quotedStr)); - str = "null and null will be emitted as "" - */ - String sJONull = XML.toString(jsonObjectJONull); - assertTrue("JSONObject.NULL should emit a null value", - "null".equals(sJONull)); - String sNull = XML.toString(jsonObjectNull); - assertTrue("null should emit an empty string", "".equals(sNull)); - } - - @Test(expected = JSONPointerException.class) - public void queryWithNoResult() { - new JSONObject().query("/a/b"); - } - - @Test - public void optQueryWithNoResult() { - assertNull(new JSONObject().optQuery("/a/b")); - } - - @Test(expected = IllegalArgumentException.class) - public void optQueryWithSyntaxError() { - new JSONObject().optQuery("invalid"); - } - - @Test(expected = JSONException.class) - public void invalidEscapeSequence() { - String json = "{ \"\\url\": \"value\" }"; - new JSONObject(json); - } - - /** - * Exercise JSONObject toMap() method. - */ - @Test - public void toMap() { - String jsonObjectStr = - "{" + - "\"key1\":" + - "[1,2," + - "{\"key3\":true}" + - "]," + - "\"key2\":" + - "{\"key1\":\"val1\",\"key2\":" + - "{\"key2\":null}," + - "\"key3\":42" + - "}," + - "\"key3\":" + - "[" + - "[\"value1\",2.1]" + - "," + - "[null]" + - "]" + - "}"; - - JSONObject jsonObject = new JSONObject(jsonObjectStr); - Map map = jsonObject.toMap(); - - assertTrue("Map should not be null", map != null); - assertTrue("Map should have 3 elements", map.size() == 3); - - List key1List = (List)map.get("key1"); - assertTrue("key1 should not be null", key1List != null); - assertTrue("key1 list should have 3 elements", key1List.size() == 3); - assertTrue("key1 value 1 should be 1", key1List.get(0).equals(Integer.valueOf(1))); - assertTrue("key1 value 2 should be 2", key1List.get(1).equals(Integer.valueOf(2))); - - Map key1Value3Map = (Map)key1List.get(2); - assertTrue("Map should not be null", key1Value3Map != null); - assertTrue("Map should have 1 element", key1Value3Map.size() == 1); - assertTrue("Map key3 should be true", key1Value3Map.get("key3").equals(Boolean.TRUE)); - - Map key2Map = (Map)map.get("key2"); - assertTrue("key2 should not be null", key2Map != null); - assertTrue("key2 map should have 3 elements", key2Map.size() == 3); - assertTrue("key2 map key 1 should be val1", key2Map.get("key1").equals("val1")); - assertTrue("key2 map key 3 should be 42", key2Map.get("key3").equals(Integer.valueOf(42))); - - Map key2Val2Map = (Map)key2Map.get("key2"); - assertTrue("key2 map key 2 should not be null", key2Val2Map != null); - assertTrue("key2 map key 2 should have an entry", key2Val2Map.containsKey("key2")); - assertTrue("key2 map key 2 value should be null", key2Val2Map.get("key2") == null); - - List key3List = (List)map.get("key3"); - assertTrue("key3 should not be null", key3List != null); - assertTrue("key3 list should have 3 elements", key3List.size() == 2); - - List key3Val1List = (List)key3List.get(0); - assertTrue("key3 list val 1 should not be null", key3Val1List != null); - assertTrue("key3 list val 1 should have 2 elements", key3Val1List.size() == 2); - assertTrue("key3 list val 1 list element 1 should be value1", key3Val1List.get(0).equals("value1")); - assertTrue("key3 list val 1 list element 2 should be 2.1", key3Val1List.get(1).equals(Double.valueOf("2.1"))); - - List key3Val2List = (List)key3List.get(1); - assertTrue("key3 list val 2 should not be null", key3Val2List != null); - assertTrue("key3 list val 2 should have 1 element", key3Val2List.size() == 1); - assertTrue("key3 list val 2 list element 1 should be null", key3Val2List.get(0) == null); - - // Assert that toMap() is a deep copy - jsonObject.getJSONArray("key3").getJSONArray(0).put(0, "still value 1"); - assertTrue("key3 list val 1 list element 1 should be value1", key3Val1List.get(0).equals("value1")); - - // assert that the new map is mutable - assertTrue("Removing a key should succeed", map.remove("key3") != null); - assertTrue("Map should have 2 elements", map.size() == 2); - - } -} +package org.json.junit; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.io.StringReader; +import java.io.StringWriter; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +import org.json.CDL; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.json.JSONPointerException; +import org.json.XML; +import org.junit.Test; + +import com.jayway.jsonpath.Configuration; +import com.jayway.jsonpath.JsonPath; + +/** + * JSONObject, along with JSONArray, are the central classes of the reference app. + * All of the other classes interact with them, and JSON functionality would + * otherwise be impossible. + */ +public class JSONObjectTest { + + /** + * JSONObject built from a bean, but only using a null value. + * Nothing good is expected to happen. + * Expects NullPointerException + */ + @Test(expected=NullPointerException.class) + public void jsonObjectByNullBean() { + assertNull("Expected an exception",new JSONObject((MyBean)null)); + } + + /** + * The JSON parser is permissive of unambiguous unquoted keys and values. + * Such JSON text should be allowed, even if it does not strictly conform + * to the spec. However, after being parsed, toString() should emit strictly + * conforming JSON text. + */ + @Test + public void unquotedText() { + String str = "{key1:value1, key2:42}"; + JSONObject jsonObject = new JSONObject(str); + String textStr = jsonObject.toString(); + assertTrue("expected key1", textStr.contains("\"key1\"")); + assertTrue("expected value1", textStr.contains("\"value1\"")); + assertTrue("expected key2", textStr.contains("\"key2\"")); + assertTrue("expected 42", textStr.contains("42")); + } + + @Test + public void testLongFromString(){ + String str = "26315000000253009"; + JSONObject json = new JSONObject(); + json.put("key", str); + + final Object actualKey = json.opt("key"); + assert str.equals(actualKey) : "Incorrect key value. Got " + actualKey + + " expected " + str; + + final long actualLong = json.optLong("key"); + assert actualLong != 0 : "Unable to extract long value for string " + str; + assert 26315000000253009L == actualLong : "Incorrect key value. Got " + + actualLong + " expected " + str; + + final String actualString = json.optString("key"); + assert str.equals(actualString) : "Incorrect key value. Got " + + actualString + " expected " + str; + } + + /** + * A JSONObject can be created with no content + */ + @Test + public void emptyJsonObject() { + JSONObject jsonObject = new JSONObject(); + assertTrue("jsonObject should be empty", jsonObject.length() == 0); + } + + /** + * A JSONObject can be created from another JSONObject plus a list of names. + * In this test, some of the starting JSONObject keys are not in the + * names list. + */ + @Test + public void jsonObjectByNames() { + String str = + "{"+ + "\"trueKey\":true,"+ + "\"falseKey\":false,"+ + "\"nullKey\":null,"+ + "\"stringKey\":\"hello world!\","+ + "\"escapeStringKey\":\"h\be\tllo w\u1234orld!\","+ + "\"intKey\":42,"+ + "\"doubleKey\":-23.45e67"+ + "}"; + String[] keys = {"falseKey", "stringKey", "nullKey", "doubleKey"}; + JSONObject jsonObject = new JSONObject(str); + + // validate JSON + JSONObject jsonObjectByName = new JSONObject(jsonObject, keys); + Object doc = Configuration.defaultConfiguration().jsonProvider().parse(jsonObjectByName.toString()); + assertTrue("expected 4 top level items", ((Map)(JsonPath.read(doc, "$"))).size() == 4); + assertTrue("expected \"falseKey\":false", Boolean.FALSE.equals(jsonObjectByName.query("/falseKey"))); + assertTrue("expected \"nullKey\":null", JSONObject.NULL.equals(jsonObjectByName.query("/nullKey"))); + assertTrue("expected \"stringKey\":\"hello world!\"", "hello world!".equals(jsonObjectByName.query("/stringKey"))); + assertTrue("expected \"doubleKey\":-23.45e67", Double.valueOf("-23.45e67").equals(jsonObjectByName.query("/doubleKey"))); + } + + /** + * JSONObjects can be built from a Map. + * In this test the map is null. + * the JSONObject(JsonTokener) ctor is not tested directly since it already + * has full coverage from other tests. + */ + @Test + public void jsonObjectByNullMap() { + Map map = null; + JSONObject jsonObject = new JSONObject(map); + assertTrue("jsonObject should be empty", jsonObject.length() == 0); + } + + /** + * JSONObjects can be built from a Map. + * In this test all of the map entries are valid JSON types. + */ + @Test + public void jsonObjectByMap() { + Map map = new HashMap(); + map.put("trueKey", new Boolean(true)); + map.put("falseKey", new Boolean(false)); + map.put("stringKey", "hello world!"); + map.put("escapeStringKey", "h\be\tllo w\u1234orld!"); + map.put("intKey", new Long(42)); + map.put("doubleKey", new Double(-23.45e67)); + JSONObject jsonObject = new JSONObject(map); + + // validate JSON + Object doc = Configuration.defaultConfiguration().jsonProvider().parse(jsonObject.toString()); + assertTrue("expected 6 top level items", ((Map)(JsonPath.read(doc, "$"))).size() == 6); + assertTrue("expected \"trueKey\":true", Boolean.TRUE.equals(jsonObject.query("/trueKey"))); + assertTrue("expected \"falseKey\":false", Boolean.FALSE.equals(jsonObject.query("/falseKey"))); + assertTrue("expected \"stringKey\":\"hello world!\"", "hello world!".equals(jsonObject.query("/stringKey"))); + assertTrue("expected \"escapeStringKey\":\"h\be\tllo w\u1234orld!\"", "h\be\tllo w\u1234orld!".equals(jsonObject.query("/escapeStringKey"))); + assertTrue("expected \"doubleKey\":-23.45e67", Double.valueOf("-23.45e67").equals(jsonObject.query("/doubleKey"))); + } + + /** + * Verifies that the constructor has backwards compatability with RAW types pre-java5. + */ + @Test + public void verifyConstructor() { + + final JSONObject expected = new JSONObject("{\"myKey\":10}"); + + @SuppressWarnings("rawtypes") + Map myRawC = Collections.singletonMap("myKey", Integer.valueOf(10)); + JSONObject jaRaw = new JSONObject(myRawC); + + Map myCStrObj = Collections.singletonMap("myKey", + (Object) Integer.valueOf(10)); + JSONObject jaStrObj = new JSONObject(myCStrObj); + + Map myCStrInt = Collections.singletonMap("myKey", + Integer.valueOf(10)); + JSONObject jaStrInt = new JSONObject(myCStrInt); + + Map myCObjObj = Collections.singletonMap((Object) "myKey", + (Object) Integer.valueOf(10)); + JSONObject jaObjObj = new JSONObject(myCObjObj); + + assertTrue( + "The RAW Collection should give me the same as the Typed Collection", + expected.similar(jaRaw)); + assertTrue( + "The RAW Collection should give me the same as the Typed Collection", + expected.similar(jaStrObj)); + assertTrue( + "The RAW Collection should give me the same as the Typed Collection", + expected.similar(jaStrInt)); + assertTrue( + "The RAW Collection should give me the same as the Typed Collection", + expected.similar(jaObjObj)); + } + + /** + * Tests Number serialization. + */ + @Test + public void verifyNumberOutput(){ + /** + * MyNumberContainer is a POJO, so call JSONObject(bean), + * which builds a map of getter names/values + * The only getter is getMyNumber (key=myNumber), + * whose return value is MyNumber. MyNumber extends Number, + * but is not recognized as such by wrap() per current + * implementation, so wrap() returns the default new JSONObject(bean). + * The only getter is getNumber (key=number), whose return value is + * BigDecimal(42). + */ + JSONObject jsonObject = new JSONObject(new MyNumberContainer()); + String actual = jsonObject.toString(); + String expected = "{\"myNumber\":{\"number\":42}}"; + assertEquals("Not Equal", expected , actual); + + /** + * JSONObject.put() handles objects differently than the + * bean constructor. Where the bean ctor wraps objects before + * placing them in the map, put() inserts the object without wrapping. + * In this case, a MyNumber instance is the value. + * The MyNumber.toString() method is responsible for + * returning a reasonable value: the string '42'. + */ + jsonObject = new JSONObject(); + jsonObject.put("myNumber", new MyNumber()); + actual = jsonObject.toString(); + expected = "{\"myNumber\":42}"; + assertEquals("Not Equal", expected , actual); + + /** + * Calls the JSONObject(Map) ctor, which calls wrap() for values. + * AtomicInteger is a Number, but is not recognized by wrap(), per + * current implementation. However, the type is + * 'java.util.concurrent.atomic', so due to the 'java' prefix, + * wrap() inserts the value as a string. That is why 42 comes back + * wrapped in quotes. + */ + jsonObject = new JSONObject(Collections.singletonMap("myNumber", new AtomicInteger(42))); + actual = jsonObject.toString(); + expected = "{\"myNumber\":\"42\"}"; + assertEquals("Not Equal", expected , actual); + + /** + * JSONObject.put() inserts the AtomicInteger directly into the + * map not calling wrap(). In toString()->write()->writeValue(), + * AtomicInteger is recognized as a Number, and converted via + * numberToString() into the unquoted string '42'. + */ + jsonObject = new JSONObject(); + jsonObject.put("myNumber", new AtomicInteger(42)); + actual = jsonObject.toString(); + expected = "{\"myNumber\":42}"; + assertEquals("Not Equal", expected , actual); + + /** + * Calls the JSONObject(Map) ctor, which calls wrap() for values. + * Fraction is a Number, but is not recognized by wrap(), per + * current implementation. As a POJO, Fraction is handled as a + * bean and inserted into a contained JSONObject. It has 2 getters, + * for numerator and denominator. + */ + jsonObject = new JSONObject(Collections.singletonMap("myNumber", new Fraction(4,2))); + assertEquals(1, jsonObject.length()); + assertEquals(2, ((JSONObject)(jsonObject.get("myNumber"))).length()); + assertEquals("Numerator", BigInteger.valueOf(4) , jsonObject.query("/myNumber/numerator")); + assertEquals("Denominator", BigInteger.valueOf(2) , jsonObject.query("/myNumber/denominator")); + + /** + * JSONObject.put() inserts the Fraction directly into the + * map not calling wrap(). In toString()->write()->writeValue(), + * Fraction is recognized as a Number, and converted via + * numberToString() into the unquoted string '4/2'. But the + * BigDecimal sanity check fails, so writeValue() defaults + * to returning a safe JSON quoted string. Pretty slick! + */ + jsonObject = new JSONObject(); + jsonObject.put("myNumber", new Fraction(4,2)); + actual = jsonObject.toString(); + expected = "{\"myNumber\":\"4/2\"}"; // valid JSON, bug fixed + assertEquals("Not Equal", expected , actual); + } + + /** + * Verifies that the put Collection has backwards compatibility with RAW types pre-java5. + */ + @Test + public void verifyPutCollection() { + + final JSONObject expected = new JSONObject("{\"myCollection\":[10]}"); + + @SuppressWarnings("rawtypes") + Collection myRawC = Collections.singleton(Integer.valueOf(10)); + JSONObject jaRaw = new JSONObject(); + jaRaw.put("myCollection", myRawC); + + Collection myCObj = Collections.singleton((Object) Integer + .valueOf(10)); + JSONObject jaObj = new JSONObject(); + jaObj.put("myCollection", myCObj); + + Collection myCInt = Collections.singleton(Integer + .valueOf(10)); + JSONObject jaInt = new JSONObject(); + jaInt.put("myCollection", myCInt); + + assertTrue( + "The RAW Collection should give me the same as the Typed Collection", + expected.similar(jaRaw)); + assertTrue( + "The RAW Collection should give me the same as the Typed Collection", + expected.similar(jaObj)); + assertTrue( + "The RAW Collection should give me the same as the Typed Collection", + expected.similar(jaInt)); + } + + + /** + * Verifies that the put Map has backwards compatability with RAW types pre-java5. + */ + @Test + public void verifyPutMap() { + + final JSONObject expected = new JSONObject("{\"myMap\":{\"myKey\":10}}"); + + @SuppressWarnings("rawtypes") + Map myRawC = Collections.singletonMap("myKey", Integer.valueOf(10)); + JSONObject jaRaw = new JSONObject(); + jaRaw.put("myMap", myRawC); + + Map myCStrObj = Collections.singletonMap("myKey", + (Object) Integer.valueOf(10)); + JSONObject jaStrObj = new JSONObject(); + jaStrObj.put("myMap", myCStrObj); + + Map myCStrInt = Collections.singletonMap("myKey", + Integer.valueOf(10)); + JSONObject jaStrInt = new JSONObject(); + jaStrInt.put("myMap", myCStrInt); + + Map myCObjObj = Collections.singletonMap((Object) "myKey", + (Object) Integer.valueOf(10)); + JSONObject jaObjObj = new JSONObject(); + jaObjObj.put("myMap", myCObjObj); + + assertTrue( + "The RAW Collection should give me the same as the Typed Collection", + expected.similar(jaRaw)); + assertTrue( + "The RAW Collection should give me the same as the Typed Collection", + expected.similar(jaStrObj)); + assertTrue( + "The RAW Collection should give me the same as the Typed Collection", + expected.similar(jaStrInt)); + assertTrue( + "The RAW Collection should give me the same as the Typed Collection", + expected.similar(jaObjObj)); + } + + + /** + * JSONObjects can be built from a Map. + * In this test the map entries are not valid JSON types. + * The actual conversion is kind of interesting. + */ + @Test + public void jsonObjectByMapWithUnsupportedValues() { + Map jsonMap = new HashMap(); + // Just insert some random objects + jsonMap.put("key1", new CDL()); + jsonMap.put("key2", new Exception()); + + JSONObject jsonObject = new JSONObject(jsonMap); + + // validate JSON + Object doc = Configuration.defaultConfiguration().jsonProvider().parse(jsonObject.toString()); + assertTrue("expected 2 top level items", ((Map)(JsonPath.read(doc, "$"))).size() == 2); + assertTrue("expected 0 key1 items", ((Map)(JsonPath.read(doc, "$.key1"))).size() == 0); + assertTrue("expected \"key2\":java.lang.Exception","java.lang.Exception".equals(jsonObject.query("/key2"))); + } + + /** + * JSONObjects can be built from a Map. + * In this test one of the map values is null + */ + @Test + public void jsonObjectByMapWithNullValue() { + Map map = new HashMap(); + map.put("trueKey", new Boolean(true)); + map.put("falseKey", new Boolean(false)); + map.put("stringKey", "hello world!"); + map.put("nullKey", null); + map.put("escapeStringKey", "h\be\tllo w\u1234orld!"); + map.put("intKey", new Long(42)); + map.put("doubleKey", new Double(-23.45e67)); + JSONObject jsonObject = new JSONObject(map); + + // validate JSON + Object doc = Configuration.defaultConfiguration().jsonProvider().parse(jsonObject.toString()); + assertTrue("expected 6 top level items", ((Map)(JsonPath.read(doc, "$"))).size() == 6); + assertTrue("expected \"trueKey\":true", Boolean.TRUE.equals(jsonObject.query("/trueKey"))); + assertTrue("expected \"falseKey\":false", Boolean.FALSE.equals(jsonObject.query("/falseKey"))); + assertTrue("expected \"stringKey\":\"hello world!\"", "hello world!".equals(jsonObject.query("/stringKey"))); + assertTrue("expected \"escapeStringKey\":\"h\be\tllo w\u1234orld!\"", "h\be\tllo w\u1234orld!".equals(jsonObject.query("/escapeStringKey"))); + assertTrue("expected \"intKey\":42", Long.valueOf("42").equals(jsonObject.query("/intKey"))); + assertTrue("expected \"doubleKey\":-23.45e67", Double.valueOf("-23.45e67").equals(jsonObject.query("/doubleKey"))); + } + + /** + * JSONObject built from a bean. In this case all but one of the + * bean getters return valid JSON types + */ + @SuppressWarnings("boxing") + @Test + public void jsonObjectByBean() { + /** + * Default access classes have to be mocked since JSONObject, which is + * not in the same package, cannot call MyBean methods by reflection. + */ + MyBean myBean = mock(MyBean.class); + when(myBean.getDoubleKey()).thenReturn(-23.45e7); + when(myBean.getIntKey()).thenReturn(42); + when(myBean.getStringKey()).thenReturn("hello world!"); + when(myBean.getEscapeStringKey()).thenReturn("h\be\tllo w\u1234orld!"); + when(myBean.isTrueKey()).thenReturn(true); + when(myBean.isFalseKey()).thenReturn(false); + when(myBean.getStringReaderKey()).thenReturn( + new StringReader("") { + }); + + JSONObject jsonObject = new JSONObject(myBean); + + // validate JSON + Object doc = Configuration.defaultConfiguration().jsonProvider().parse(jsonObject.toString()); + assertTrue("expected 8 top level items", ((Map)(JsonPath.read(doc, "$"))).size() == 8); + assertTrue("expected 0 items in stringReaderKey", ((Map) (JsonPath.read(doc, "$.stringReaderKey"))).size() == 0); + assertTrue("expected true", Boolean.TRUE.equals(jsonObject.query("/trueKey"))); + assertTrue("expected false", Boolean.FALSE.equals(jsonObject.query("/falseKey"))); + assertTrue("expected hello world!","hello world!".equals(jsonObject.query("/stringKey"))); + assertTrue("expected h\be\tllo w\u1234orld!", "h\be\tllo w\u1234orld!".equals(jsonObject.query("/escapeStringKey"))); + assertTrue("expected 42", Integer.valueOf("42").equals(jsonObject.query("/intKey"))); + assertTrue("expected -23.45e7", Double.valueOf("-23.45e7").equals(jsonObject.query("/doubleKey"))); + // sorry, mockito artifact + assertTrue("expected 2 callbacks items", ((List)(JsonPath.read(doc, "$.callbacks"))).size() == 2); + assertTrue("expected 0 handler items", ((Map)(JsonPath.read(doc, "$.callbacks[0].handler"))).size() == 0); + assertTrue("expected 0 callbacks[1] items", ((Map)(JsonPath.read(doc, "$.callbacks[1]"))).size() == 0); + } + + /** + * A bean is also an object. But in order to test the JSONObject + * ctor that takes an object and a list of names, + * this particular bean needs some public + * data members, which have been added to the class. + */ + @Test + public void jsonObjectByObjectAndNames() { + String[] keys = {"publicString", "publicInt"}; + // just need a class that has public data members + MyPublicClass myPublicClass = new MyPublicClass(); + JSONObject jsonObject = new JSONObject(myPublicClass, keys); + + // validate JSON + Object doc = Configuration.defaultConfiguration().jsonProvider().parse(jsonObject.toString()); + assertTrue("expected 2 top level items", ((Map)(JsonPath.read(doc, "$"))).size() == 2); + assertTrue("expected \"publicString\":\"abc\"", "abc".equals(jsonObject.query("/publicString"))); + assertTrue("expected \"publicInt\":42", Integer.valueOf(42).equals(jsonObject.query("/publicInt"))); + } + + /** + * Exercise the JSONObject from resource bundle functionality. + * The test resource bundle is uncomplicated, but provides adequate test coverage. + */ + @Test + public void jsonObjectByResourceBundle() { + JSONObject jsonObject = new + JSONObject("org.json.junit.StringsResourceBundle", + Locale.getDefault()); + + // validate JSON + Object doc = Configuration.defaultConfiguration().jsonProvider().parse(jsonObject.toString()); + assertTrue("expected 2 top level items", ((Map)(JsonPath.read(doc, "$"))).size() == 2); + assertTrue("expected 2 greetings items", ((Map)(JsonPath.read(doc, "$.greetings"))).size() == 2); + assertTrue("expected \"hello\":\"Hello, \"", "Hello, ".equals(jsonObject.query("/greetings/hello"))); + assertTrue("expected \"world\":\"World!\"", "World!".equals(jsonObject.query("/greetings/world"))); + assertTrue("expected 2 farewells items", ((Map)(JsonPath.read(doc, "$.farewells"))).size() == 2); + assertTrue("expected \"later\":\"Later, \"", "Later, ".equals(jsonObject.query("/farewells/later"))); + assertTrue("expected \"world\":\"World!\"", "Alligator!".equals(jsonObject.query("/farewells/gator"))); + } + + /** + * Exercise the JSONObject.accumulate() method + */ + @SuppressWarnings("boxing") + @Test + public void jsonObjectAccumulate() { + + JSONObject jsonObject = new JSONObject(); + jsonObject.accumulate("myArray", true); + jsonObject.accumulate("myArray", false); + jsonObject.accumulate("myArray", "hello world!"); + jsonObject.accumulate("myArray", "h\be\tllo w\u1234orld!"); + jsonObject.accumulate("myArray", 42); + jsonObject.accumulate("myArray", -23.45e7); + // include an unsupported object for coverage + try { + jsonObject.accumulate("myArray", Double.NaN); + assertTrue("Expected exception", false); + } catch (JSONException ignored) {} + + // validate JSON + Object doc = Configuration.defaultConfiguration().jsonProvider().parse(jsonObject.toString()); + assertTrue("expected 1 top level item", ((Map)(JsonPath.read(doc, "$"))).size() == 1); + assertTrue("expected 6 myArray items", ((List)(JsonPath.read(doc, "$.myArray"))).size() == 6); + assertTrue("expected true", Boolean.TRUE.equals(jsonObject.query("/myArray/0"))); + assertTrue("expected false", Boolean.FALSE.equals(jsonObject.query("/myArray/1"))); + assertTrue("expected hello world!", "hello world!".equals(jsonObject.query("/myArray/2"))); + assertTrue("expected h\be\tllo w\u1234orld!", "h\be\tllo w\u1234orld!".equals(jsonObject.query("/myArray/3"))); + assertTrue("expected 42", Integer.valueOf(42).equals(jsonObject.query("/myArray/4"))); + assertTrue("expected -23.45e7", Double.valueOf(-23.45e7).equals(jsonObject.query("/myArray/5"))); + } + + /** + * Exercise the JSONObject append() functionality + */ + @SuppressWarnings("boxing") + @Test + public void jsonObjectAppend() { + JSONObject jsonObject = new JSONObject(); + jsonObject.append("myArray", true); + jsonObject.append("myArray", false); + jsonObject.append("myArray", "hello world!"); + jsonObject.append("myArray", "h\be\tllo w\u1234orld!"); + jsonObject.append("myArray", 42); + jsonObject.append("myArray", -23.45e7); + // include an unsupported object for coverage + try { + jsonObject.append("myArray", Double.NaN); + assertTrue("Expected exception", false); + } catch (JSONException ignored) {} + + // validate JSON + Object doc = Configuration.defaultConfiguration().jsonProvider().parse(jsonObject.toString()); + assertTrue("expected 1 top level item", ((Map)(JsonPath.read(doc, "$"))).size() == 1); + assertTrue("expected 6 myArray items", ((List)(JsonPath.read(doc, "$.myArray"))).size() == 6); + assertTrue("expected true", Boolean.TRUE.equals(jsonObject.query("/myArray/0"))); + assertTrue("expected false", Boolean.FALSE.equals(jsonObject.query("/myArray/1/"))); + assertTrue("expected hello world!", "hello world!".equals(jsonObject.query("/myArray/2"))); + assertTrue("expected h\be\tllo w\u1234orld!", "h\be\tllo w\u1234orld!".equals(jsonObject.query("/myArray/3"))); + assertTrue("expected 42", Integer.valueOf(42).equals(jsonObject.query("/myArray/4"))); + assertTrue("expected -23.45e7", Double.valueOf(-23.45e7).equals(jsonObject.query("/myArray/5"))); + } + + /** + * Exercise the JSONObject doubleToString() method + */ + @SuppressWarnings("boxing") + @Test + public void jsonObjectDoubleToString() { + String [] expectedStrs = {"1", "1", "-23.4", "-2.345E68", "null", "null" }; + Double [] doubles = { 1.0, 00001.00000, -23.4, -23.45e67, + Double.NaN, Double.NEGATIVE_INFINITY }; + for (int i = 0; i < expectedStrs.length; ++i) { + String actualStr = JSONObject.doubleToString(doubles[i]); + assertTrue("value expected ["+expectedStrs[i]+ + "] found ["+actualStr+ "]", + expectedStrs[i].equals(actualStr)); + } + } + + /** + * Exercise some JSONObject get[type] and opt[type] methods + */ + @Test + public void jsonObjectValues() { + String str = + "{"+ + "\"trueKey\":true,"+ + "\"falseKey\":false,"+ + "\"trueStrKey\":\"true\","+ + "\"falseStrKey\":\"false\","+ + "\"stringKey\":\"hello world!\","+ + "\"intKey\":42,"+ + "\"intStrKey\":\"43\","+ + "\"longKey\":1234567890123456789,"+ + "\"longStrKey\":\"987654321098765432\","+ + "\"doubleKey\":-23.45e7,"+ + "\"doubleStrKey\":\"00001.000\","+ + "\"arrayKey\":[0,1,2],"+ + "\"objectKey\":{\"myKey\":\"myVal\"}"+ + "}"; + JSONObject jsonObject = new JSONObject(str); + assertTrue("trueKey should be true", jsonObject.getBoolean("trueKey")); + assertTrue("opt trueKey should be true", jsonObject.optBoolean("trueKey")); + assertTrue("falseKey should be false", !jsonObject.getBoolean("falseKey")); + assertTrue("trueStrKey should be true", jsonObject.getBoolean("trueStrKey")); + assertTrue("trueStrKey should be true", jsonObject.optBoolean("trueStrKey")); + assertTrue("falseStrKey should be false", !jsonObject.getBoolean("falseStrKey")); + assertTrue("stringKey should be string", + jsonObject.getString("stringKey").equals("hello world!")); + assertTrue("doubleKey should be double", + jsonObject.getDouble("doubleKey") == -23.45e7); + assertTrue("doubleStrKey should be double", + jsonObject.getDouble("doubleStrKey") == 1); + assertTrue("opt doubleKey should be double", + jsonObject.optDouble("doubleKey") == -23.45e7); + assertTrue("opt doubleKey with Default should be double", + jsonObject.optDouble("doubleStrKey", Double.NaN) == 1); + assertTrue("intKey should be int", + jsonObject.optInt("intKey") == 42); + assertTrue("opt intKey should be int", + jsonObject.optInt("intKey", 0) == 42); + assertTrue("opt intKey with default should be int", + jsonObject.getInt("intKey") == 42); + assertTrue("intStrKey should be int", + jsonObject.getInt("intStrKey") == 43); + assertTrue("longKey should be long", + jsonObject.getLong("longKey") == 1234567890123456789L); + assertTrue("opt longKey should be long", + jsonObject.optLong("longKey") == 1234567890123456789L); + assertTrue("opt longKey with default should be long", + jsonObject.optLong("longKey", 0) == 1234567890123456789L); + assertTrue("longStrKey should be long", + jsonObject.getLong("longStrKey") == 987654321098765432L); + assertTrue("xKey should not exist", + jsonObject.isNull("xKey")); + assertTrue("stringKey should exist", + jsonObject.has("stringKey")); + assertTrue("opt stringKey should string", + jsonObject.optString("stringKey").equals("hello world!")); + assertTrue("opt stringKey with default should string", + jsonObject.optString("stringKey", "not found").equals("hello world!")); + JSONArray jsonArray = jsonObject.getJSONArray("arrayKey"); + assertTrue("arrayKey should be JSONArray", + jsonArray.getInt(0) == 0 && + jsonArray.getInt(1) == 1 && + jsonArray.getInt(2) == 2); + jsonArray = jsonObject.optJSONArray("arrayKey"); + assertTrue("opt arrayKey should be JSONArray", + jsonArray.getInt(0) == 0 && + jsonArray.getInt(1) == 1 && + jsonArray.getInt(2) == 2); + JSONObject jsonObjectInner = jsonObject.getJSONObject("objectKey"); + assertTrue("objectKey should be JSONObject", + jsonObjectInner.get("myKey").equals("myVal")); + } + + /** + * Check whether JSONObject handles large or high precision numbers correctly + */ + @Test + public void stringToValueNumbersTest() { + assertTrue("-0 Should be a Double!",JSONObject.stringToValue("-0") instanceof Double); + assertTrue("-0.0 Should be a Double!",JSONObject.stringToValue("-0.0") instanceof Double); + assertTrue("'-' Should be a String!",JSONObject.stringToValue("-") instanceof String); + assertTrue( "0.2 should be a Double!", + JSONObject.stringToValue( "0.2" ) instanceof Double ); + assertTrue( "Doubles should be Doubles, even when incorrectly converting floats!", + JSONObject.stringToValue( new Double( "0.2f" ).toString() ) instanceof Double ); + /** + * This test documents a need for BigDecimal conversion. + */ + Object obj = JSONObject.stringToValue( "299792.457999999984" ); + assertTrue( "evaluates to 299792.458 doubld instead of 299792.457999999984 BigDecimal!", + obj.equals(new Double(299792.458)) ); + assertTrue( "1 should be an Integer!", + JSONObject.stringToValue( "1" ) instanceof Integer ); + assertTrue( "Integer.MAX_VALUE should still be an Integer!", + JSONObject.stringToValue( new Integer( Integer.MAX_VALUE ).toString() ) instanceof Integer ); + assertTrue( "Large integers should be a Long!", + JSONObject.stringToValue( new Long( Long.sum( Integer.MAX_VALUE, 1 ) ).toString() ) instanceof Long ); + assertTrue( "Long.MAX_VALUE should still be an Integer!", + JSONObject.stringToValue( new Long( Long.MAX_VALUE ).toString() ) instanceof Long ); + + String str = new BigInteger( new Long( Long.MAX_VALUE ).toString() ).add( BigInteger.ONE ).toString(); + assertTrue( "Really large integers currently evaluate to string", + JSONObject.stringToValue(str).equals("9223372036854775808")); + } + + /** + * This test documents numeric values which could be numerically + * handled as BigDecimal or BigInteger. It helps determine what outputs + * will change if those types are supported. + */ + @Test + public void jsonValidNumberValuesNeitherLongNorIEEE754Compatible() { + // Valid JSON Numbers, probably should return BigDecimal or BigInteger objects + String str = + "{"+ + "\"numberWithDecimals\":299792.457999999984,"+ + "\"largeNumber\":12345678901234567890,"+ + "\"preciseNumber\":0.2000000000000000111,"+ + "\"largeExponent\":-23.45e2327"+ + "}"; + JSONObject jsonObject = new JSONObject(str); + // Comes back as a double, but loses precision + assertTrue( "numberWithDecimals currently evaluates to double 299792.458", + jsonObject.get( "numberWithDecimals" ).equals( new Double( "299792.458" ) ) ); + Object obj = jsonObject.get( "largeNumber" ); + assertTrue("largeNumber currently evaluates to string", + "12345678901234567890".equals(obj)); + // comes back as a double but loses precision + assertTrue( "preciseNumber currently evaluates to double 0.2", + jsonObject.get( "preciseNumber" ).equals(new Double(0.2))); + obj = jsonObject.get( "largeExponent" ); + assertTrue("largeExponent should currently evaluates as a string", + "-23.45e2327".equals(obj)); + } + + /** + * This test documents how JSON-Java handles invalid numeric input. + */ + @Test + public void jsonInvalidNumberValues() { + // Number-notations supported by Java and invalid as JSON + String str = + "{"+ + "\"hexNumber\":-0x123,"+ + "\"tooManyZeros\":00,"+ + "\"negativeInfinite\":-Infinity,"+ + "\"negativeNaN\":-NaN,"+ + "\"negativeFraction\":-.01,"+ + "\"tooManyZerosFraction\":00.001,"+ + "\"negativeHexFloat\":-0x1.fffp1,"+ + "\"hexFloat\":0x1.0P-1074,"+ + "\"floatIdentifier\":0.1f,"+ + "\"doubleIdentifier\":0.1d"+ + "}"; + JSONObject jsonObject = new JSONObject(str); + Object obj; + obj = jsonObject.get( "hexNumber" ); + assertFalse( "hexNumber must not be a number (should throw exception!?)", + obj instanceof Number ); + assertTrue("hexNumber currently evaluates to string", + obj.equals("-0x123")); + assertTrue( "tooManyZeros currently evaluates to string", + jsonObject.get( "tooManyZeros" ).equals("00")); + obj = jsonObject.get("negativeInfinite"); + assertTrue( "negativeInfinite currently evaluates to string", + obj.equals("-Infinity")); + obj = jsonObject.get("negativeNaN"); + assertTrue( "negativeNaN currently evaluates to string", + obj.equals("-NaN")); + assertTrue( "negativeFraction currently evaluates to double -0.01", + jsonObject.get( "negativeFraction" ).equals(new Double(-0.01))); + assertTrue( "tooManyZerosFraction currently evaluates to double 0.001", + jsonObject.get( "tooManyZerosFraction" ).equals(new Double(0.001))); + assertTrue( "negativeHexFloat currently evaluates to double -3.99951171875", + jsonObject.get( "negativeHexFloat" ).equals(new Double(-3.99951171875))); + assertTrue("hexFloat currently evaluates to double 4.9E-324", + jsonObject.get("hexFloat").equals(new Double(4.9E-324))); + assertTrue("floatIdentifier currently evaluates to double 0.1", + jsonObject.get("floatIdentifier").equals(new Double(0.1))); + assertTrue("doubleIdentifier currently evaluates to double 0.1", + jsonObject.get("doubleIdentifier").equals(new Double(0.1))); + } + + /** + * Tests how JSONObject get[type] handles incorrect types + */ + @Test + public void jsonObjectNonAndWrongValues() { + String str = + "{"+ + "\"trueKey\":true,"+ + "\"falseKey\":false,"+ + "\"trueStrKey\":\"true\","+ + "\"falseStrKey\":\"false\","+ + "\"stringKey\":\"hello world!\","+ + "\"intKey\":42,"+ + "\"intStrKey\":\"43\","+ + "\"longKey\":1234567890123456789,"+ + "\"longStrKey\":\"987654321098765432\","+ + "\"doubleKey\":-23.45e7,"+ + "\"doubleStrKey\":\"00001.000\","+ + "\"arrayKey\":[0,1,2],"+ + "\"objectKey\":{\"myKey\":\"myVal\"}"+ + "}"; + JSONObject jsonObject = new JSONObject(str); + try { + jsonObject.getBoolean("nonKey"); + assertTrue("Expected an exception", false); + } catch (JSONException e) { + assertTrue("expecting an exception message", + "JSONObject[\"nonKey\"] not found.".equals(e.getMessage())); + } + try { + jsonObject.getBoolean("stringKey"); + assertTrue("Expected an exception", false); + } catch (JSONException e) { + assertTrue("Expecting an exception message", + "JSONObject[\"stringKey\"] is not a Boolean.". + equals(e.getMessage())); + } + try { + jsonObject.getString("nonKey"); + assertTrue("Expected an exception", false); + } catch (JSONException e) { + assertTrue("Expecting an exception message", + "JSONObject[\"nonKey\"] not found.". + equals(e.getMessage())); + } + try { + jsonObject.getString("trueKey"); + assertTrue("Expected an exception", false); + } catch (JSONException e) { + assertTrue("Expecting an exception message", + "JSONObject[\"trueKey\"] not a string.". + equals(e.getMessage())); + } + try { + jsonObject.getDouble("nonKey"); + assertTrue("Expected an exception", false); + } catch (JSONException e) { + assertTrue("Expecting an exception message", + "JSONObject[\"nonKey\"] not found.". + equals(e.getMessage())); + } + try { + jsonObject.getDouble("stringKey"); + assertTrue("Expected an exception", false); + } catch (JSONException e) { + assertTrue("Expecting an exception message", + "JSONObject[\"stringKey\"] is not a number.". + equals(e.getMessage())); + } + try { + jsonObject.getInt("nonKey"); + assertTrue("Expected an exception", false); + } catch (JSONException e) { + assertTrue("Expecting an exception message", + "JSONObject[\"nonKey\"] not found.". + equals(e.getMessage())); + } + try { + jsonObject.getInt("stringKey"); + assertTrue("Expected an exception", false); + } catch (JSONException e) { + assertTrue("Expecting an exception message", + "JSONObject[\"stringKey\"] is not an int.". + equals(e.getMessage())); + } + try { + jsonObject.getLong("nonKey"); + assertTrue("Expected an exception", false); + } catch (JSONException e) { + assertTrue("Expecting an exception message", + "JSONObject[\"nonKey\"] not found.". + equals(e.getMessage())); + } + try { + jsonObject.getLong("stringKey"); + assertTrue("Expected an exception", false); + } catch (JSONException e) { + assertTrue("Expecting an exception message", + "JSONObject[\"stringKey\"] is not a long.". + equals(e.getMessage())); + } + try { + jsonObject.getJSONArray("nonKey"); + assertTrue("Expected an exception", false); + } catch (JSONException e) { + assertTrue("Expecting an exception message", + "JSONObject[\"nonKey\"] not found.". + equals(e.getMessage())); + } + try { + jsonObject.getJSONArray("stringKey"); + assertTrue("Expected an exception", false); + } catch (JSONException e) { + assertTrue("Expecting an exception message", + "JSONObject[\"stringKey\"] is not a JSONArray.". + equals(e.getMessage())); + } + try { + jsonObject.getJSONObject("nonKey"); + assertTrue("Expected an exception", false); + } catch (JSONException e) { + assertTrue("Expecting an exception message", + "JSONObject[\"nonKey\"] not found.". + equals(e.getMessage())); + } + try { + jsonObject.getJSONObject("stringKey"); + assertTrue("Expected an exception", false); + } catch (JSONException e) { + assertTrue("Expecting an exception message", + "JSONObject[\"stringKey\"] is not a JSONObject.". + equals(e.getMessage())); + } + } + + /** + * This test documents an unexpected numeric behavior. + * A double that ends with .0 is parsed, serialized, then + * parsed again. On the second parse, it has become an int. + */ + @Test + public void unexpectedDoubleToIntConversion() { + String key30 = "key30"; + String key31 = "key31"; + JSONObject jsonObject = new JSONObject(); + jsonObject.put(key30, new Double(3.0)); + jsonObject.put(key31, new Double(3.1)); + + assertTrue("3.0 should remain a double", + jsonObject.getDouble(key30) == 3); + assertTrue("3.1 should remain a double", + jsonObject.getDouble(key31) == 3.1); + + // turns 3.0 into 3. + String serializedString = jsonObject.toString(); + JSONObject deserialized = new JSONObject(serializedString); + assertTrue("3.0 is now an int", deserialized.get(key30) instanceof Integer); + assertTrue("3.0 can still be interpreted as a double", + deserialized.getDouble(key30) == 3.0); + assertTrue("3.1 remains a double", deserialized.getDouble(key31) == 3.1); + } + + /** + * Document behaviors of big numbers. Includes both JSONObject + * and JSONArray tests + */ + @SuppressWarnings("boxing") + @Test + public void bigNumberOperations() { + /** + * JSONObject tries to parse BigInteger as a bean, but it only has + * one getter, getLowestBitSet(). The value is lost and an unhelpful + * value is stored. This should be fixed. + */ + BigInteger bigInteger = new BigInteger("123456789012345678901234567890"); + JSONObject jsonObject = new JSONObject(bigInteger); + Object obj = jsonObject.get("lowestSetBit"); + assertTrue("JSONObject only has 1 value", jsonObject.length() == 1); + assertTrue("JSONObject parses BigInteger as the Integer lowestBitSet", + obj instanceof Integer); + assertTrue("this bigInteger lowestBitSet happens to be 1", + obj.equals(1)); + + /** + * JSONObject tries to parse BigDecimal as a bean, but it has + * no getters, The value is lost and no value is stored. + * This should be fixed. + */ + BigDecimal bigDecimal = new BigDecimal( + "123456789012345678901234567890.12345678901234567890123456789"); + jsonObject = new JSONObject(bigDecimal); + assertTrue("large bigDecimal is not stored", jsonObject.length() == 0); + + /** + * JSONObject put(String, Object) method stores and serializes + * bigInt and bigDec correctly. Nothing needs to change. + */ + jsonObject = new JSONObject(); + jsonObject.put("bigInt", bigInteger); + assertTrue("jsonObject.put() handles bigInt correctly", + jsonObject.get("bigInt").equals(bigInteger)); + assertTrue("jsonObject.getBigInteger() handles bigInt correctly", + jsonObject.getBigInteger("bigInt").equals(bigInteger)); + assertTrue("jsonObject.optBigInteger() handles bigInt correctly", + jsonObject.optBigInteger("bigInt", BigInteger.ONE).equals(bigInteger)); + assertTrue("jsonObject serializes bigInt correctly", + jsonObject.toString().equals("{\"bigInt\":123456789012345678901234567890}")); + jsonObject = new JSONObject(); + jsonObject.put("bigDec", bigDecimal); + assertTrue("jsonObject.put() handles bigDec correctly", + jsonObject.get("bigDec").equals(bigDecimal)); + assertTrue("jsonObject.getBigDecimal() handles bigDec correctly", + jsonObject.getBigDecimal("bigDec").equals(bigDecimal)); + assertTrue("jsonObject.optBigDecimal() handles bigDec correctly", + jsonObject.optBigDecimal("bigDec", BigDecimal.ONE).equals(bigDecimal)); + assertTrue("jsonObject serializes bigDec correctly", + jsonObject.toString().equals( + "{\"bigDec\":123456789012345678901234567890.12345678901234567890123456789}")); + + /** + * exercise some exceptions + */ + try { + jsonObject.getBigDecimal("bigInt"); + assertTrue("expected an exeption", false); + } catch (JSONException ignored) {} + obj = jsonObject.optBigDecimal("bigInt", BigDecimal.ONE); + assertTrue("expected BigDecimal", obj.equals(BigDecimal.ONE)); + try { + jsonObject.getBigInteger("bigDec"); + assertTrue("expected an exeption", false); + } catch (JSONException ignored) {} + jsonObject.put("stringKey", "abc"); + try { + jsonObject.getBigDecimal("stringKey"); + assertTrue("expected an exeption", false); + } catch (JSONException ignored) {} + obj = jsonObject.optBigInteger("bigDec", BigInteger.ONE); + assertTrue("expected BigInteger", obj.equals(BigInteger.ONE)); + + /** + * JSONObject.numberToString() works correctly, nothing to change. + */ + String str = JSONObject.numberToString(bigInteger); + assertTrue("numberToString() handles bigInteger correctly", + str.equals("123456789012345678901234567890")); + str = JSONObject.numberToString(bigDecimal); + assertTrue("numberToString() handles bigDecimal correctly", + str.equals("123456789012345678901234567890.12345678901234567890123456789")); + + /** + * JSONObject.stringToValue() turns bigInt into an accurate string, + * and rounds bigDec. This incorrect, but users may have come to + * expect this behavior. Change would be marginally better, but + * might inconvenience users. + */ + obj = JSONObject.stringToValue(bigInteger.toString()); + assertTrue("stringToValue() turns bigInteger string into string", + obj instanceof String); + obj = JSONObject.stringToValue(bigDecimal.toString()); + assertTrue("stringToValue() changes bigDecimal string", + !obj.toString().equals(bigDecimal.toString())); + + /** + * wrap() vs put() big number behavior is now the same. + */ + // bigInt map ctor + Map map = new HashMap(); + map.put("bigInt", bigInteger); + jsonObject = new JSONObject(map); + String actualFromMapStr = jsonObject.toString(); + assertTrue("bigInt in map (or array or bean) is a string", + actualFromMapStr.equals( + "{\"bigInt\":123456789012345678901234567890}")); + // bigInt put + jsonObject = new JSONObject(); + jsonObject.put("bigInt", bigInteger); + String actualFromPutStr = jsonObject.toString(); + assertTrue("bigInt from put is a number", + actualFromPutStr.equals( + "{\"bigInt\":123456789012345678901234567890}")); + // bigDec map ctor + map = new HashMap(); + map.put("bigDec", bigDecimal); + jsonObject = new JSONObject(map); + actualFromMapStr = jsonObject.toString(); + assertTrue("bigDec in map (or array or bean) is a bigDec", + actualFromMapStr.equals( + "{\"bigDec\":123456789012345678901234567890.12345678901234567890123456789}")); + // bigDec put + jsonObject = new JSONObject(); + jsonObject.put("bigDec", bigDecimal); + actualFromPutStr = jsonObject.toString(); + assertTrue("bigDec from put is a number", + actualFromPutStr.equals( + "{\"bigDec\":123456789012345678901234567890.12345678901234567890123456789}")); + // bigInt,bigDec put + JSONArray jsonArray = new JSONArray(); + jsonArray.put(bigInteger); + jsonArray.put(bigDecimal); + actualFromPutStr = jsonArray.toString(); + assertTrue("bigInt, bigDec from put is a number", + actualFromPutStr.equals( + "[123456789012345678901234567890,123456789012345678901234567890.12345678901234567890123456789]")); + assertTrue("getBigInt is bigInt", jsonArray.getBigInteger(0).equals(bigInteger)); + assertTrue("getBigDec is bigDec", jsonArray.getBigDecimal(1).equals(bigDecimal)); + assertTrue("optBigInt is bigInt", jsonArray.optBigInteger(0, BigInteger.ONE).equals(bigInteger)); + assertTrue("optBigDec is bigDec", jsonArray.optBigDecimal(1, BigDecimal.ONE).equals(bigDecimal)); + jsonArray.put(Boolean.TRUE); + try { + jsonArray.getBigInteger(2); + assertTrue("should not be able to get big int", false); + } catch (Exception ignored) {} + try { + jsonArray.getBigDecimal(2); + assertTrue("should not be able to get big dec", false); + } catch (Exception ignored) {} + assertTrue("optBigInt is default", jsonArray.optBigInteger(2, BigInteger.ONE).equals(BigInteger.ONE)); + assertTrue("optBigDec is default", jsonArray.optBigDecimal(2, BigDecimal.ONE).equals(BigDecimal.ONE)); + + // bigInt,bigDec list ctor + List list = new ArrayList(); + list.add(bigInteger); + list.add(bigDecimal); + jsonArray = new JSONArray(list); + String actualFromListStr = jsonArray.toString(); + assertTrue("bigInt, bigDec in list is a bigInt, bigDec", + actualFromListStr.equals( + "[123456789012345678901234567890,123456789012345678901234567890.12345678901234567890123456789]")); + // bigInt bean ctor + MyBigNumberBean myBigNumberBean = mock(MyBigNumberBean.class); + when(myBigNumberBean.getBigInteger()).thenReturn(new BigInteger("123456789012345678901234567890")); + jsonObject = new JSONObject(myBigNumberBean); + String actualFromBeanStr = jsonObject.toString(); + // can't do a full string compare because mockery adds an extra key/value + assertTrue("bigInt from bean ctor is a bigInt", + actualFromBeanStr.contains("123456789012345678901234567890")); + // bigDec bean ctor + myBigNumberBean = mock(MyBigNumberBean.class); + when(myBigNumberBean.getBigDecimal()).thenReturn(new BigDecimal("123456789012345678901234567890.12345678901234567890123456789")); + jsonObject = new JSONObject(myBigNumberBean); + actualFromBeanStr = jsonObject.toString(); + // can't do a full string compare because mockery adds an extra key/value + assertTrue("bigDec from bean ctor is a bigDec", + actualFromBeanStr.contains("123456789012345678901234567890.12345678901234567890123456789")); + // bigInt,bigDec wrap() + obj = JSONObject.wrap(bigInteger); + assertTrue("wrap() returns big num",obj.equals(bigInteger)); + obj = JSONObject.wrap(bigDecimal); + assertTrue("wrap() returns string",obj.equals(bigDecimal)); + + } + + /** + * The purpose for the static method getNames() methods are not clear. + * This method is not called from within JSON-Java. Most likely + * uses are to prep names arrays for: + * JSONObject(JSONObject jo, String[] names) + * JSONObject(Object object, String names[]), + */ + @Test + public void jsonObjectNames() { + JSONObject jsonObject; + + // getNames() from null JSONObject + assertTrue("null names from null Object", + null == JSONObject.getNames((Object)null)); + + // getNames() from object with no fields + assertTrue("null names from Object with no fields", + null == JSONObject.getNames(new MyJsonString())); + + // getNames from new JSONOjbect + jsonObject = new JSONObject(); + String [] names = JSONObject.getNames(jsonObject); + assertTrue("names should be null", names == null); + + + // getNames() from empty JSONObject + String emptyStr = "{}"; + jsonObject = new JSONObject(emptyStr); + assertTrue("empty JSONObject should have null names", + null == JSONObject.getNames(jsonObject)); + + // getNames() from JSONObject + String str = + "{"+ + "\"trueKey\":true,"+ + "\"falseKey\":false,"+ + "\"stringKey\":\"hello world!\","+ + "}"; + jsonObject = new JSONObject(str); + names = JSONObject.getNames(jsonObject); + JSONArray jsonArray = new JSONArray(names); + + // validate JSON + Object doc = Configuration.defaultConfiguration().jsonProvider() + .parse(jsonArray.toString()); + List docList = JsonPath.read(doc, "$"); + assertTrue("expected 3 items", docList.size() == 3); + assertTrue( + "expected to find trueKey", + ((List) JsonPath.read(doc, "$[?(@=='trueKey')]")).size() == 1); + assertTrue( + "expected to find falseKey", + ((List) JsonPath.read(doc, "$[?(@=='falseKey')]")).size() == 1); + assertTrue( + "expected to find stringKey", + ((List) JsonPath.read(doc, "$[?(@=='stringKey')]")).size() == 1); + + /** + * getNames() from an enum with properties has an interesting result. + * It returns the enum values, not the selected enum properties + */ + MyEnumField myEnumField = MyEnumField.VAL1; + names = JSONObject.getNames(myEnumField); + + // validate JSON + jsonArray = new JSONArray(names); + doc = Configuration.defaultConfiguration().jsonProvider() + .parse(jsonArray.toString()); + docList = JsonPath.read(doc, "$"); + assertTrue("expected 3 items", docList.size() == 3); + assertTrue( + "expected to find VAL1", + ((List) JsonPath.read(doc, "$[?(@=='VAL1')]")).size() == 1); + assertTrue( + "expected to find VAL2", + ((List) JsonPath.read(doc, "$[?(@=='VAL2')]")).size() == 1); + assertTrue( + "expected to find VAL3", + ((List) JsonPath.read(doc, "$[?(@=='VAL3')]")).size() == 1); + + /** + * A bean is also an object. But in order to test the static + * method getNames(), this particular bean needs some public + * data members. + */ + MyPublicClass myPublicClass = new MyPublicClass(); + names = JSONObject.getNames(myPublicClass); + + // validate JSON + jsonArray = new JSONArray(names); + doc = Configuration.defaultConfiguration().jsonProvider() + .parse(jsonArray.toString()); + docList = JsonPath.read(doc, "$"); + assertTrue("expected 2 items", docList.size() == 2); + assertTrue( + "expected to find publicString", + ((List) JsonPath.read(doc, "$[?(@=='publicString')]")).size() == 1); + assertTrue( + "expected to find publicInt", + ((List) JsonPath.read(doc, "$[?(@=='publicInt')]")).size() == 1); + } + + /** + * Populate a JSONArray from an empty JSONObject names() method. + * It should be empty. + */ + @Test + public void emptyJsonObjectNamesToJsonAray() { + JSONObject jsonObject = new JSONObject(); + JSONArray jsonArray = jsonObject.names(); + assertTrue("jsonArray should be null", jsonArray == null); + } + + /** + * Populate a JSONArray from a JSONObject names() method. + * Confirm that it contains the expected names. + */ + @Test + public void jsonObjectNamesToJsonAray() { + String str = + "{"+ + "\"trueKey\":true,"+ + "\"falseKey\":false,"+ + "\"stringKey\":\"hello world!\","+ + "}"; + + JSONObject jsonObject = new JSONObject(str); + JSONArray jsonArray = jsonObject.names(); + + // validate JSON + Object doc = Configuration.defaultConfiguration().jsonProvider().parse(jsonArray.toString()); + assertTrue("expected 3 top level items", ((List)(JsonPath.read(doc, "$"))).size() == 3); + assertTrue("expected to find trueKey", ((List) JsonPath.read(doc, "$[?(@=='trueKey')]")).size() == 1); + assertTrue("expected to find falseKey", ((List) JsonPath.read(doc, "$[?(@=='falseKey')]")).size() == 1); + assertTrue("expected to find stringKey", ((List) JsonPath.read(doc, "$[?(@=='stringKey')]")).size() == 1); + } + + /** + * Exercise the JSONObject increment() method. + */ + @SuppressWarnings("cast") + @Test + public void jsonObjectIncrement() { + String str = + "{"+ + "\"keyLong\":9999999991,"+ + "\"keyDouble\":1.1"+ + "}"; + JSONObject jsonObject = new JSONObject(str); + jsonObject.increment("keyInt"); + jsonObject.increment("keyInt"); + jsonObject.increment("keyLong"); + jsonObject.increment("keyDouble"); + jsonObject.increment("keyInt"); + jsonObject.increment("keyLong"); + jsonObject.increment("keyDouble"); + /** + * JSONObject constructor won't handle these types correctly, but + * adding them via put works. + */ + jsonObject.put("keyFloat", new Float(1.1)); + jsonObject.put("keyBigInt", new BigInteger("123456789123456789123456789123456780")); + jsonObject.put("keyBigDec", new BigDecimal("123456789123456789123456789123456780.1")); + jsonObject.increment("keyFloat"); + jsonObject.increment("keyFloat"); + jsonObject.increment("keyBigInt"); + jsonObject.increment("keyBigDec"); + + // validate JSON + Object doc = Configuration.defaultConfiguration().jsonProvider().parse(jsonObject.toString()); + assertTrue("expected 6 top level items", ((Map)(JsonPath.read(doc, "$"))).size() == 6); + assertTrue("expected 3", Integer.valueOf(3).equals(jsonObject.query("/keyInt"))); + assertTrue("expected 9999999993", Long.valueOf(9999999993L).equals(jsonObject.query("/keyLong"))); + assertTrue("expected 3.1", Double.valueOf(3.1).equals(jsonObject.query("/keyDouble"))); + assertTrue("expected 123456789123456789123456789123456781", new BigInteger("123456789123456789123456789123456781").equals(jsonObject.query("/keyBigInt"))); + assertTrue("expected 123456789123456789123456789123456781.1", new BigDecimal("123456789123456789123456789123456781.1").equals(jsonObject.query("/keyBigDec"))); + + /** + * Should work the same way on any platform! @see https://docs.oracle + * .com/javase/specs/jls/se7/html/jls-4.html#jls-4.2.3 This is the + * effect of a float to double conversion and is inherent to the + * shortcomings of the IEEE 754 format, when converting 32-bit into + * double-precision 64-bit. Java type-casts float to double. A 32 bit + * float is type-casted to 64 bit double by simply appending zero-bits + * to the mantissa (and extended the signed exponent by 3 bits.) and + * there is no way to obtain more information than it is stored in the + * 32-bits float. + * + * Like 1/3 cannot be represented as base10 number because it is + * periodically, 1/5 (for example) cannot be represented as base2 number + * since it is periodically in base2 (take a look at + * http://www.h-schmidt.net/FloatConverter/) The same happens to 3.1, + * that decimal number (base10 representation) is periodic in base2 + * representation, therefore appending zero-bits is inaccurate. Only + * repeating the periodically occurring bits (0110) would be a proper + * conversion. However one cannot detect from a 32 bit IEE754 + * representation which bits would "repeat infinitely", since the + * missing bits would not fit into the 32 bit float, i.e. the + * information needed simply is not there! + */ + assertTrue("expected 3.0999999046325684", Double.valueOf(3.0999999046325684).equals(jsonObject.query("/keyFloat"))); + + /** + * float f = 3.1f; double df = (double) f; double d = 3.1d; + * System.out.println + * (Integer.toBinaryString(Float.floatToRawIntBits(f))); + * System.out.println + * (Long.toBinaryString(Double.doubleToRawLongBits(df))); + * System.out.println + * (Long.toBinaryString(Double.doubleToRawLongBits(d))); + * + * - Float: + * seeeeeeeemmmmmmmmmmmmmmmmmmmmmmm + * 1000000010001100110011001100110 + * - Double + * seeeeeeeeeeemmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm + * 10000000 10001100110011001100110 + * 100000000001000110011001100110011000000000000000000000000000000 + * 100000000001000110011001100110011001100110011001100110011001101 + */ + + /** + * Examples of well documented but probably unexpected behavior in + * java / with 32-bit float to 64-bit float conversion. + */ + assertFalse("Document unexpected behaviour with explicit type-casting float as double!", (double)0.2f == 0.2d ); + assertFalse("Document unexpected behaviour with implicit type-cast!", 0.2f == 0.2d ); + Double d1 = new Double( 1.1f ); + Double d2 = new Double( "1.1f" ); + assertFalse( "Document implicit type cast from float to double before calling Double(double d) constructor", d1.equals( d2 ) ); + + assertTrue( "Correctly converting float to double via base10 (string) representation!", new Double( 3.1d ).equals( new Double( new Float( 3.1f ).toString() ) ) ); + + // Pinpointing the not so obvious "buggy" conversion from float to double in JSONObject + JSONObject jo = new JSONObject(); + jo.put( "bug", 3.1f ); // will call put( String key, double value ) with implicit and "buggy" type-cast from float to double + assertFalse( "The java-compiler did add some zero bits for you to the mantissa (unexpected, but well documented)", jo.get( "bug" ).equals( new Double( 3.1d ) ) ); + + JSONObject inc = new JSONObject(); + inc.put( "bug", new Float( 3.1f ) ); // This will put in instance of Float into JSONObject, i.e. call put( String key, Object value ) + assertTrue( "Everything is ok here!", inc.get( "bug" ) instanceof Float ); + inc.increment( "bug" ); // after adding 1, increment will call put( String key, double value ) with implicit and "buggy" type-cast from float to double! + // this.put(key, (Float) value + 1); + // 1. The (Object)value will be typecasted to (Float)value since it is an instanceof Float actually nothing is done. + // 2. Float instance will be autoboxed into float because the + operator will work on primitives not Objects! + // 3. A float+float operation will be performed and results into a float primitive. + // 4. There is no method that matches the signature put( String key, float value), java-compiler will choose the method + // put( String key, double value) and does an implicit type-cast(!) by appending zero-bits to the mantissa + assertTrue( "JSONObject increment converts Float to Double", jo.get( "bug" ) instanceof Double ); + // correct implementation (with change of behavior) would be: + // this.put(key, new Float((Float) value + 1)); + // Probably it would be better to deprecate the method and remove some day, while convenient processing the "payload" is not + // really in the the scope of a JSON-library (IMHO.) + + } + + /** + * Exercise JSONObject numberToString() method + */ + @SuppressWarnings("boxing") + @Test + public void jsonObjectNumberToString() { + String str; + Double dVal; + Integer iVal = 1; + str = JSONObject.numberToString(iVal); + assertTrue("expected "+iVal+" actual "+str, iVal.toString().equals(str)); + dVal = 12.34; + str = JSONObject.numberToString(dVal); + assertTrue("expected "+dVal+" actual "+str, dVal.toString().equals(str)); + dVal = 12.34e27; + str = JSONObject.numberToString(dVal); + assertTrue("expected "+dVal+" actual "+str, dVal.toString().equals(str)); + // trailing .0 is truncated, so it doesn't quite match toString() + dVal = 5000000.0000000; + str = JSONObject.numberToString(dVal); + assertTrue("expected 5000000 actual "+str, str.equals("5000000")); + } + + /** + * Exercise JSONObject put() and similar() methods + */ + @SuppressWarnings("boxing") + @Test + public void jsonObjectPut() { + String expectedStr = + "{"+ + "\"trueKey\":true,"+ + "\"falseKey\":false,"+ + "\"arrayKey\":[0,1,2],"+ + "\"objectKey\":{"+ + "\"myKey1\":\"myVal1\","+ + "\"myKey2\":\"myVal2\","+ + "\"myKey3\":\"myVal3\","+ + "\"myKey4\":\"myVal4\""+ + "}"+ + "}"; + JSONObject jsonObject = new JSONObject(); + jsonObject.put("trueKey", true); + jsonObject.put("falseKey", false); + Integer [] intArray = { 0, 1, 2 }; + jsonObject.put("arrayKey", Arrays.asList(intArray)); + Map myMap = new HashMap(); + myMap.put("myKey1", "myVal1"); + myMap.put("myKey2", "myVal2"); + myMap.put("myKey3", "myVal3"); + myMap.put("myKey4", "myVal4"); + jsonObject.put("objectKey", myMap); + + // validate JSON + Object doc = Configuration.defaultConfiguration().jsonProvider().parse(jsonObject.toString()); + assertTrue("expected 4 top level items", ((Map)(JsonPath.read(doc, "$"))).size() == 4); + assertTrue("expected true", Boolean.TRUE.equals(jsonObject.query("/trueKey"))); + assertTrue("expected false", Boolean.FALSE.equals(jsonObject.query("/falseKey"))); + assertTrue("expected 3 arrayKey items", ((List)(JsonPath.read(doc, "$.arrayKey"))).size() == 3); + assertTrue("expected 0", Integer.valueOf(0).equals(jsonObject.query("/arrayKey/0"))); + assertTrue("expected 1", Integer.valueOf(1).equals(jsonObject.query("/arrayKey/1"))); + assertTrue("expected 2", Integer.valueOf(2).equals(jsonObject.query("/arrayKey/2"))); + assertTrue("expected 4 objectKey items", ((Map)(JsonPath.read(doc, "$.objectKey"))).size() == 4); + assertTrue("expected myVal1", "myVal1".equals(jsonObject.query("/objectKey/myKey1"))); + assertTrue("expected myVal2", "myVal2".equals(jsonObject.query("/objectKey/myKey2"))); + assertTrue("expected myVal3", "myVal3".equals(jsonObject.query("/objectKey/myKey3"))); + assertTrue("expected myVal4", "myVal4".equals(jsonObject.query("/objectKey/myKey4"))); + + jsonObject.remove("trueKey"); + JSONObject expectedJsonObject = new JSONObject(expectedStr); + assertTrue("unequal jsonObjects should not be similar", + !jsonObject.similar(expectedJsonObject)); + assertTrue("jsonObject should not be similar to jsonArray", + !jsonObject.similar(new JSONArray())); + + String aCompareValueStr = "{\"a\":\"aval\",\"b\":true}"; + String bCompareValueStr = "{\"a\":\"notAval\",\"b\":true}"; + JSONObject aCompareValueJsonObject = new JSONObject(aCompareValueStr); + JSONObject bCompareValueJsonObject = new JSONObject(bCompareValueStr); + assertTrue("different values should not be similar", + !aCompareValueJsonObject.similar(bCompareValueJsonObject)); + + String aCompareObjectStr = "{\"a\":\"aval\",\"b\":{}}"; + String bCompareObjectStr = "{\"a\":\"aval\",\"b\":true}"; + JSONObject aCompareObjectJsonObject = new JSONObject(aCompareObjectStr); + JSONObject bCompareObjectJsonObject = new JSONObject(bCompareObjectStr); + assertTrue("different nested JSONObjects should not be similar", + !aCompareObjectJsonObject.similar(bCompareObjectJsonObject)); + + String aCompareArrayStr = "{\"a\":\"aval\",\"b\":[]}"; + String bCompareArrayStr = "{\"a\":\"aval\",\"b\":true}"; + JSONObject aCompareArrayJsonObject = new JSONObject(aCompareArrayStr); + JSONObject bCompareArrayJsonObject = new JSONObject(bCompareArrayStr); + assertTrue("different nested JSONArrays should not be similar", + !aCompareArrayJsonObject.similar(bCompareArrayJsonObject)); + } + + /** + * Exercise JSONObject toString() method + */ + @Test + public void jsonObjectToString() { + String str = + "{"+ + "\"trueKey\":true,"+ + "\"falseKey\":false,"+ + "\"arrayKey\":[0,1,2],"+ + "\"objectKey\":{"+ + "\"myKey1\":\"myVal1\","+ + "\"myKey2\":\"myVal2\","+ + "\"myKey3\":\"myVal3\","+ + "\"myKey4\":\"myVal4\""+ + "}"+ + "}"; + JSONObject jsonObject = new JSONObject(str); + + // validate JSON + Object doc = Configuration.defaultConfiguration().jsonProvider().parse(jsonObject.toString()); + assertTrue("expected 4 top level items", ((Map)(JsonPath.read(doc, "$"))).size() == 4); + assertTrue("expected true", Boolean.TRUE.equals(jsonObject.query("/trueKey"))); + assertTrue("expected false", Boolean.FALSE.equals(jsonObject.query("/falseKey"))); + assertTrue("expected 3 arrayKey items", ((List)(JsonPath.read(doc, "$.arrayKey"))).size() == 3); + assertTrue("expected 0", Integer.valueOf(0).equals(jsonObject.query("/arrayKey/0"))); + assertTrue("expected 1", Integer.valueOf(1).equals(jsonObject.query("/arrayKey/1"))); + assertTrue("expected 2", Integer.valueOf(2).equals(jsonObject.query("/arrayKey/2"))); + assertTrue("expected 4 objectKey items", ((Map)(JsonPath.read(doc, "$.objectKey"))).size() == 4); + assertTrue("expected myVal1", "myVal1".equals(jsonObject.query("/objectKey/myKey1"))); + assertTrue("expected myVal2", "myVal2".equals(jsonObject.query("/objectKey/myKey2"))); + assertTrue("expected myVal3", "myVal3".equals(jsonObject.query("/objectKey/myKey3"))); + assertTrue("expected myVal4", "myVal4".equals(jsonObject.query("/objectKey/myKey4"))); + } + + /** + * Exercise JSONObject toString() method with various indent levels. + */ + @Test + public void jsonObjectToStringIndent() { + String jsonObject0Str = + "{"+ + "\"key1\":" + + "[1,2," + + "{\"key3\":true}" + + "],"+ + "\"key2\":" + + "{\"key1\":\"val1\",\"key2\":" + + "{\"key2\":\"val2\"}" + + "},"+ + "\"key3\":" + + "[" + + "[1,2.1]" + + "," + + "[null]" + + "]"+ + "}"; + + String jsonObject1Str = + "{\n" + + " \"key1\": [\n" + + " 1,\n" + + " 2,\n" + + " {\"key3\": true}\n" + + " ],\n" + + " \"key2\": {\n" + + " \"key1\": \"val1\",\n" + + " \"key2\": {\"key2\": \"val2\"}\n" + + " },\n" + + " \"key3\": [\n" + + " [\n" + + " 1,\n" + + " 2.1\n" + + " ],\n" + + " [null]\n" + + " ]\n" + + "}"; + String jsonObject4Str = + "{\n" + + " \"key1\": [\n" + + " 1,\n" + + " 2,\n" + + " {\"key3\": true}\n" + + " ],\n" + + " \"key2\": {\n" + + " \"key1\": \"val1\",\n" + + " \"key2\": {\"key2\": \"val2\"}\n" + + " },\n" + + " \"key3\": [\n" + + " [\n" + + " 1,\n" + + " 2.1\n" + + " ],\n" + + " [null]\n" + + " ]\n" + + "}"; + JSONObject jsonObject = new JSONObject(jsonObject0Str); + assertEquals(jsonObject0Str, jsonObject.toString()); + assertEquals(jsonObject0Str, jsonObject.toString(0)); + assertEquals(jsonObject1Str, jsonObject.toString(1)); + assertEquals(jsonObject4Str, jsonObject.toString(4)); + } + + /** + * Explores how JSONObject handles maps. Insert a string/string map + * as a value in a JSONObject. It will remain a map. Convert the + * JSONObject to string, then create a new JSONObject from the string. + * In the new JSONObject, the value will be stored as a nested JSONObject. + * Confirm that map and nested JSONObject have the same contents. + */ + @Test + public void jsonObjectToStringSuppressWarningOnCastToMap() { + JSONObject jsonObject = new JSONObject(); + Map map = new HashMap<>(); + map.put("abc", "def"); + jsonObject.put("key", map); + + // validate JSON + Object doc = Configuration.defaultConfiguration().jsonProvider().parse(jsonObject.toString()); + assertTrue("expected 1 top level item", ((Map)(JsonPath.read(doc, "$"))).size() == 1); + assertTrue("expected 1 key item", ((Map)(JsonPath.read(doc, "$.key"))).size() == 1); + assertTrue("expected def", "def".equals(jsonObject.query("/key/abc"))); + } + + /** + * Explores how JSONObject handles collections. Insert a string collection + * as a value in a JSONObject. It will remain a collection. Convert the + * JSONObject to string, then create a new JSONObject from the string. + * In the new JSONObject, the value will be stored as a nested JSONArray. + * Confirm that collection and nested JSONArray have the same contents. + */ + @Test + public void jsonObjectToStringSuppressWarningOnCastToCollection() { + JSONObject jsonObject = new JSONObject(); + Collection collection = new ArrayList(); + collection.add("abc"); + // ArrayList will be added as an object + jsonObject.put("key", collection); + + // validate JSON + Object doc = Configuration.defaultConfiguration().jsonProvider().parse(jsonObject.toString()); + assertTrue("expected 1 top level item", ((Map)(JsonPath.read(doc, "$"))).size() == 1); + assertTrue("expected 1 key item", ((List)(JsonPath.read(doc, "$.key"))).size() == 1); + assertTrue("expected abc", "abc".equals(jsonObject.query("/key/0"))); + } + + /** + * Exercises the JSONObject.valueToString() method for various types + */ + @Test + public void valueToString() { + + assertTrue("null valueToString() incorrect", + "null".equals(JSONObject.valueToString(null))); + MyJsonString jsonString = new MyJsonString(); + assertTrue("jsonstring valueToString() incorrect", + "my string".equals(JSONObject.valueToString(jsonString))); + assertTrue("boolean valueToString() incorrect", + "true".equals(JSONObject.valueToString(Boolean.TRUE))); + assertTrue("non-numeric double", + "null".equals(JSONObject.doubleToString(Double.POSITIVE_INFINITY))); + String jsonObjectStr = + "{"+ + "\"key1\":\"val1\","+ + "\"key2\":\"val2\","+ + "\"key3\":\"val3\""+ + "}"; + JSONObject jsonObject = new JSONObject(jsonObjectStr); + assertTrue("jsonObject valueToString() incorrect", + JSONObject.valueToString(jsonObject).equals(jsonObject.toString())); + String jsonArrayStr = + "[1,2,3]"; + JSONArray jsonArray = new JSONArray(jsonArrayStr); + assertTrue("jsonArray valueToString() incorrect", + JSONObject.valueToString(jsonArray).equals(jsonArray.toString())); + Map map = new HashMap(); + map.put("key1", "val1"); + map.put("key2", "val2"); + map.put("key3", "val3"); + assertTrue("map valueToString() incorrect", + jsonObject.toString().equals(JSONObject.valueToString(map))); + Collection collection = new ArrayList(); + collection.add(new Integer(1)); + collection.add(new Integer(2)); + collection.add(new Integer(3)); + assertTrue("collection valueToString() expected: "+ + jsonArray.toString()+ " actual: "+ + JSONObject.valueToString(collection), + jsonArray.toString().equals(JSONObject.valueToString(collection))); + Integer[] array = { new Integer(1), new Integer(2), new Integer(3) }; + assertTrue("array valueToString() incorrect", + jsonArray.toString().equals(JSONObject.valueToString(array))); + } + + /** + * Confirm that https://github.com/douglascrockford/JSON-java/issues/167 is fixed. + * The following code was throwing a ClassCastException in the + * JSONObject(Map) constructor + */ + @SuppressWarnings("boxing") + @Test + public void valueToStringConfirmException() { + Map myMap = new HashMap(); + myMap.put(1, "myValue"); + // this is the test, it should not throw an exception + String str = JSONObject.valueToString(myMap); + // confirm result, just in case + Object doc = Configuration.defaultConfiguration().jsonProvider().parse(str); + assertTrue("expected 1 top level item", ((Map)(JsonPath.read(doc, "$"))).size() == 1); + assertTrue("expected myValue", "myValue".equals(JsonPath.read(doc, "$.1"))); + } + + /** + * Exercise the JSONObject wrap() method. Sometimes wrap() will change + * the object being wrapped, other times not. The purpose of wrap() is + * to ensure the value is packaged in a way that is compatible with how + * a JSONObject value or JSONArray value is supposed to be stored. + */ + @Test + public void wrapObject() { + // wrap(null) returns NULL + assertTrue("null wrap() incorrect", + JSONObject.NULL == JSONObject.wrap(null)); + + // wrap(Integer) returns Integer + Integer in = new Integer(1); + assertTrue("Integer wrap() incorrect", + in == JSONObject.wrap(in)); + + /** + * This test is to document the preferred behavior if BigDecimal is + * supported. Previously bd returned as a string, since it + * is recognized as being a Java package class. Now with explicit + * support for big numbers, it remains a BigDecimal + */ + Object bdWrap = JSONObject.wrap(BigDecimal.ONE); + assertTrue("BigDecimal.ONE evaluates to ONE", + bdWrap.equals(BigDecimal.ONE)); + + // wrap JSONObject returns JSONObject + String jsonObjectStr = + "{"+ + "\"key1\":\"val1\","+ + "\"key2\":\"val2\","+ + "\"key3\":\"val3\""+ + "}"; + JSONObject jsonObject = new JSONObject(jsonObjectStr); + assertTrue("JSONObject wrap() incorrect", + jsonObject == JSONObject.wrap(jsonObject)); + + // wrap collection returns JSONArray + Collection collection = new ArrayList(); + collection.add(new Integer(1)); + collection.add(new Integer(2)); + collection.add(new Integer(3)); + JSONArray jsonArray = (JSONArray) (JSONObject.wrap(collection)); + + // validate JSON + Object doc = Configuration.defaultConfiguration().jsonProvider().parse(jsonArray.toString()); + assertTrue("expected 3 top level items", ((List)(JsonPath.read(doc, "$"))).size() == 3); + assertTrue("expected 1", Integer.valueOf(1).equals(jsonArray.query("/0"))); + assertTrue("expected 2", Integer.valueOf(2).equals(jsonArray.query("/1"))); + assertTrue("expected 3", Integer.valueOf(3).equals(jsonArray.query("/2"))); + + // wrap Array returns JSONArray + Integer[] array = { new Integer(1), new Integer(2), new Integer(3) }; + JSONArray integerArrayJsonArray = (JSONArray)(JSONObject.wrap(array)); + + // validate JSON + doc = Configuration.defaultConfiguration().jsonProvider().parse(jsonArray.toString()); + assertTrue("expected 3 top level items", ((List)(JsonPath.read(doc, "$"))).size() == 3); + assertTrue("expected 1", Integer.valueOf(1).equals(jsonArray.query("/0"))); + assertTrue("expected 2", Integer.valueOf(2).equals(jsonArray.query("/1"))); + assertTrue("expected 3", Integer.valueOf(3).equals(jsonArray.query("/2"))); + + // validate JSON + doc = Configuration.defaultConfiguration().jsonProvider().parse(integerArrayJsonArray.toString()); + assertTrue("expected 3 top level items", ((List)(JsonPath.read(doc, "$"))).size() == 3); + assertTrue("expected 1", Integer.valueOf(1).equals(jsonArray.query("/0"))); + assertTrue("expected 2", Integer.valueOf(2).equals(jsonArray.query("/1"))); + assertTrue("expected 3", Integer.valueOf(3).equals(jsonArray.query("/2"))); + + // wrap map returns JSONObject + Map map = new HashMap(); + map.put("key1", "val1"); + map.put("key2", "val2"); + map.put("key3", "val3"); + JSONObject mapJsonObject = (JSONObject) (JSONObject.wrap(map)); + + // validate JSON + doc = Configuration.defaultConfiguration().jsonProvider().parse(mapJsonObject.toString()); + assertTrue("expected 3 top level items", ((Map)(JsonPath.read(doc, "$"))).size() == 3); + assertTrue("expected val1", "val1".equals(mapJsonObject.query("/key1"))); + assertTrue("expected val2", "val2".equals(mapJsonObject.query("/key2"))); + assertTrue("expected val3", "val3".equals(mapJsonObject.query("/key3"))); + } + + + /** + * RFC 7159 defines control characters to be U+0000 through U+001F. This test verifies that the parser is checking for these in expected ways. + */ + @Test + public void jsonObjectParseControlCharacters(){ + for(int i = 0;i<=0x001f;i++){ + final String charString = String.valueOf((char)i); + final String source = "{\"key\":\""+charString+"\"}"; + try { + JSONObject jo = new JSONObject(source); + assertTrue("Expected "+charString+"("+i+") in the JSON Object but did not find it.",charString.equals(jo.getString("key"))); + } catch (JSONException ex) { + assertTrue("Only \\0 (U+0000), \\n (U+000A), and \\r (U+000D) should cause an error. Instead "+charString+"("+i+") caused an error", + i=='\0' || i=='\n' || i=='\r' + ); + } + } + } + + /** + * Explore how JSONObject handles parsing errors. + */ + @SuppressWarnings("boxing") + @Test + public void jsonObjectParsingErrors() { + try { + // does not start with '{' + String str = "abc"; + assertNull("Expected an exception",new JSONObject(str)); + } catch (JSONException e) { + assertTrue("Expecting an exception message", + "A JSONObject text must begin with '{' at 1 [character 2 line 1]". + equals(e.getMessage())); + } + try { + // does not end with '}' + String str = "{"; + assertNull("Expected an exception",new JSONObject(str)); + } catch (JSONException e) { + assertTrue("Expecting an exception message", + "A JSONObject text must end with '}' at 2 [character 3 line 1]". + equals(e.getMessage())); + } + try { + // key with no ':' + String str = "{\"myKey\" = true}"; + assertNull("Expected an exception",new JSONObject(str)); + } catch (JSONException e) { + assertTrue("Expecting an exception message", + "Expected a ':' after a key at 10 [character 11 line 1]". + equals(e.getMessage())); + } + try { + // entries with no ',' separator + String str = "{\"myKey\":true \"myOtherKey\":false}"; + assertNull("Expected an exception",new JSONObject(str)); + } catch (JSONException e) { + assertTrue("Expecting an exception message", + "Expected a ',' or '}' at 15 [character 16 line 1]". + equals(e.getMessage())); + } + try { + // append to wrong key + String str = "{\"myKey\":true, \"myOtherKey\":false}"; + JSONObject jsonObject = new JSONObject(str); + jsonObject.append("myKey", "hello"); + assertTrue("Expected an exception", false); + } catch (JSONException e) { + assertTrue("Expecting an exception message", + "JSONObject[myKey] is not a JSONArray.". + equals(e.getMessage())); + } + try { + // increment wrong key + String str = "{\"myKey\":true, \"myOtherKey\":false}"; + JSONObject jsonObject = new JSONObject(str); + jsonObject.increment("myKey"); + assertTrue("Expected an exception", false); + } catch (JSONException e) { + assertTrue("Expecting an exception message", + "Unable to increment [\"myKey\"].". + equals(e.getMessage())); + } + try { + // invalid key + String str = "{\"myKey\":true, \"myOtherKey\":false}"; + JSONObject jsonObject = new JSONObject(str); + jsonObject.get(null); + assertTrue("Expected an exception", false); + } catch (JSONException e) { + assertTrue("Expecting an exception message", + "Null key.". + equals(e.getMessage())); + } + try { + // invalid numberToString() + JSONObject.numberToString((Number)null); + assertTrue("Expected an exception", false); + } catch (JSONException e) { + assertTrue("Expecting an exception message", + "Null pointer". + equals(e.getMessage())); + } + try { + // null put key + JSONObject jsonObject = new JSONObject("{}"); + jsonObject.put(null, 0); + assertTrue("Expected an exception", false); + } catch (NullPointerException ignored) { + } + try { + // multiple putOnce key + JSONObject jsonObject = new JSONObject("{}"); + jsonObject.putOnce("hello", "world"); + jsonObject.putOnce("hello", "world!"); + assertTrue("Expected an exception", false); + } catch (JSONException e) { + assertTrue("", true); + } + try { + // test validity of invalid double + JSONObject.testValidity(Double.NaN); + assertTrue("Expected an exception", false); + } catch (JSONException e) { + assertTrue("", true); + } + try { + // test validity of invalid float + JSONObject.testValidity(Float.NEGATIVE_INFINITY); + assertTrue("Expected an exception", false); + } catch (JSONException e) { + assertTrue("", true); + } + } + + /** + * Confirm behavior when putOnce() is called with null parameters + */ + @Test + public void jsonObjectPutOnceNull() { + JSONObject jsonObject = new JSONObject(); + jsonObject.putOnce(null, null); + assertTrue("jsonObject should be empty", jsonObject.length() == 0); + } + + /** + * Exercise JSONObject opt(key, default) method. + */ + @Test + public void jsonObjectOptDefault() { + + String str = "{\"myKey\": \"myval\", \"hiKey\": null}"; + JSONObject jsonObject = new JSONObject(str); + + assertTrue("optBigDecimal() should return default BigDecimal", + BigDecimal.TEN.compareTo(jsonObject.optBigDecimal("myKey", BigDecimal.TEN))==0); + assertTrue("optBigInteger() should return default BigInteger", + BigInteger.TEN.compareTo(jsonObject.optBigInteger("myKey",BigInteger.TEN ))==0); + assertTrue("optBoolean() should return default boolean", + jsonObject.optBoolean("myKey", true)); + assertTrue("optInt() should return default int", + 42 == jsonObject.optInt("myKey", 42)); + assertTrue("optEnum() should return default Enum", + MyEnum.VAL1.equals(jsonObject.optEnum(MyEnum.class, "myKey", MyEnum.VAL1))); + assertTrue("optJSONArray() should return null ", + null==jsonObject.optJSONArray("myKey")); + assertTrue("optJSONObject() should return null ", + null==jsonObject.optJSONObject("myKey")); + assertTrue("optLong() should return default long", + 42 == jsonObject.optLong("myKey", 42)); + assertTrue("optDouble() should return default double", + 42.3 == jsonObject.optDouble("myKey", 42.3)); + assertTrue("optString() should return default string", + "hi".equals(jsonObject.optString("hiKey", "hi"))); + } + + /** + * Exercise JSONObject opt(key, default) method when the key doesn't exist. + */ + @Test + public void jsonObjectOptNoKey() { + + JSONObject jsonObject = new JSONObject(); + + assertTrue("optBigDecimal() should return default BigDecimal", + BigDecimal.TEN.compareTo(jsonObject.optBigDecimal("myKey", BigDecimal.TEN))==0); + assertTrue("optBigInteger() should return default BigInteger", + BigInteger.TEN.compareTo(jsonObject.optBigInteger("myKey",BigInteger.TEN ))==0); + assertTrue("optBoolean() should return default boolean", + jsonObject.optBoolean("myKey", true)); + assertTrue("optInt() should return default int", + 42 == jsonObject.optInt("myKey", 42)); + assertTrue("optEnum() should return default Enum", + MyEnum.VAL1.equals(jsonObject.optEnum(MyEnum.class, "myKey", MyEnum.VAL1))); + assertTrue("optJSONArray() should return null ", + null==jsonObject.optJSONArray("myKey")); + assertTrue("optJSONObject() should return null ", + null==jsonObject.optJSONObject("myKey")); + assertTrue("optLong() should return default long", + 42 == jsonObject.optLong("myKey", 42)); + assertTrue("optDouble() should return default double", + 42.3 == jsonObject.optDouble("myKey", 42.3)); + assertTrue("optString() should return default string", + "hi".equals(jsonObject.optString("hiKey", "hi"))); + } + + /** + * Verifies that the opt methods properly convert string values. + */ + @Test + public void jsonObjectOptStringConversion() { + JSONObject jo = new JSONObject("{\"int\":\"123\",\"true\":\"true\",\"false\":\"false\"}"); + assertTrue("unexpected optBoolean value",jo.optBoolean("true",false)==true); + assertTrue("unexpected optBoolean value",jo.optBoolean("false",true)==false); + assertTrue("unexpected optInt value",jo.optInt("int",0)==123); + assertTrue("unexpected optLong value",jo.optLong("int",0)==123); + assertTrue("unexpected optDouble value",jo.optDouble("int",0.0)==123.0); + assertTrue("unexpected optBigInteger value",jo.optBigInteger("int",BigInteger.ZERO).compareTo(new BigInteger("123"))==0); + assertTrue("unexpected optBigDecimal value",jo.optBigDecimal("int",BigDecimal.ZERO).compareTo(new BigDecimal("123"))==0); + + } + + /** + * Confirm behavior when JSONObject put(key, null object) is called + */ + @Test + public void jsonObjectputNull() { + + // put null should remove the item. + String str = "{\"myKey\": \"myval\"}"; + JSONObject jsonObjectRemove = new JSONObject(str); + jsonObjectRemove.remove("myKey"); + + JSONObject jsonObjectPutNull = new JSONObject(str); + jsonObjectPutNull.put("myKey", (Object) null); + + // validate JSON + assertTrue("jsonObject should be empty", jsonObjectRemove.length() == 0 + && jsonObjectPutNull.length() == 0); + } + + /** + * Exercise JSONObject quote() method + * This purpose of quote() is to ensure that for strings with embedded + * quotes, the quotes are properly escaped. + */ + @Test + public void jsonObjectQuote() { + String str; + str = ""; + String quotedStr; + quotedStr = JSONObject.quote(str); + assertTrue("quote() expected escaped quotes, found "+quotedStr, + "\"\"".equals(quotedStr)); + str = "\"\""; + quotedStr = JSONObject.quote(str); + assertTrue("quote() expected escaped quotes, found "+quotedStr, + "\"\\\"\\\"\"".equals(quotedStr)); + str = "null and null will be emitted as "" + */ + String sJONull = XML.toString(jsonObjectJONull); + assertTrue("JSONObject.NULL should emit a null value", + "null".equals(sJONull)); + String sNull = XML.toString(jsonObjectNull); + assertTrue("null should emit an empty string", "".equals(sNull)); + } + + @Test(expected = JSONPointerException.class) + public void queryWithNoResult() { + new JSONObject().query("/a/b"); + } + + @Test + public void optQueryWithNoResult() { + assertNull(new JSONObject().optQuery("/a/b")); + } + + @Test(expected = IllegalArgumentException.class) + public void optQueryWithSyntaxError() { + new JSONObject().optQuery("invalid"); + } + + @Test(expected = JSONException.class) + public void invalidEscapeSequence() { + String json = "{ \"\\url\": \"value\" }"; + assertNull("Expected an exception",new JSONObject(json)); + } + + /** + * Exercise JSONObject toMap() method. + */ + @Test + public void toMap() { + String jsonObjectStr = + "{" + + "\"key1\":" + + "[1,2," + + "{\"key3\":true}" + + "]," + + "\"key2\":" + + "{\"key1\":\"val1\",\"key2\":" + + "{\"key2\":null}," + + "\"key3\":42" + + "}," + + "\"key3\":" + + "[" + + "[\"value1\",2.1]" + + "," + + "[null]" + + "]" + + "}"; + + JSONObject jsonObject = new JSONObject(jsonObjectStr); + Map map = jsonObject.toMap(); + + assertTrue("Map should not be null", map != null); + assertTrue("Map should have 3 elements", map.size() == 3); + + List key1List = (List)map.get("key1"); + assertTrue("key1 should not be null", key1List != null); + assertTrue("key1 list should have 3 elements", key1List.size() == 3); + assertTrue("key1 value 1 should be 1", key1List.get(0).equals(Integer.valueOf(1))); + assertTrue("key1 value 2 should be 2", key1List.get(1).equals(Integer.valueOf(2))); + + Map key1Value3Map = (Map)key1List.get(2); + assertTrue("Map should not be null", key1Value3Map != null); + assertTrue("Map should have 1 element", key1Value3Map.size() == 1); + assertTrue("Map key3 should be true", key1Value3Map.get("key3").equals(Boolean.TRUE)); + + Map key2Map = (Map)map.get("key2"); + assertTrue("key2 should not be null", key2Map != null); + assertTrue("key2 map should have 3 elements", key2Map.size() == 3); + assertTrue("key2 map key 1 should be val1", key2Map.get("key1").equals("val1")); + assertTrue("key2 map key 3 should be 42", key2Map.get("key3").equals(Integer.valueOf(42))); + + Map key2Val2Map = (Map)key2Map.get("key2"); + assertTrue("key2 map key 2 should not be null", key2Val2Map != null); + assertTrue("key2 map key 2 should have an entry", key2Val2Map.containsKey("key2")); + assertTrue("key2 map key 2 value should be null", key2Val2Map.get("key2") == null); + + List key3List = (List)map.get("key3"); + assertTrue("key3 should not be null", key3List != null); + assertTrue("key3 list should have 3 elements", key3List.size() == 2); + + List key3Val1List = (List)key3List.get(0); + assertTrue("key3 list val 1 should not be null", key3Val1List != null); + assertTrue("key3 list val 1 should have 2 elements", key3Val1List.size() == 2); + assertTrue("key3 list val 1 list element 1 should be value1", key3Val1List.get(0).equals("value1")); + assertTrue("key3 list val 1 list element 2 should be 2.1", key3Val1List.get(1).equals(Double.valueOf("2.1"))); + + List key3Val2List = (List)key3List.get(1); + assertTrue("key3 list val 2 should not be null", key3Val2List != null); + assertTrue("key3 list val 2 should have 1 element", key3Val2List.size() == 1); + assertTrue("key3 list val 2 list element 1 should be null", key3Val2List.get(0) == null); + + // Assert that toMap() is a deep copy + jsonObject.getJSONArray("key3").getJSONArray(0).put(0, "still value 1"); + assertTrue("key3 list val 1 list element 1 should be value1", key3Val1List.get(0).equals("value1")); + + // assert that the new map is mutable + assertTrue("Removing a key should succeed", map.remove("key3") != null); + assertTrue("Map should have 2 elements", map.size() == 2); + + } +} diff --git a/src/test/java/org/json/junit/JSONStringTest.java b/src/test/java/org/json/junit/JSONStringTest.java index cba5d09..617b9e3 100644 --- a/src/test/java/org/json/junit/JSONStringTest.java +++ b/src/test/java/org/json/junit/JSONStringTest.java @@ -1,310 +1,371 @@ -package org.json.junit; - -import static org.junit.Assert.*; - -import java.io.StringWriter; -import java.util.*; - -import org.json.*; -import org.junit.Test; - -/** - * Tests for JSONString implementations, and the difference between - * {@link JSONObject#valueToString} and {@link JSONObject#writeValue}. - */ -public class JSONStringTest { - - /** - * This tests the JSONObject.writeValue() method. We can't test directly - * due to it being a package-protected method. Instead, we can call - * JSONArray.write(), which delegates the writing of each entry to - * writeValue(). - */ - @Test - public void writeValues() throws Exception { - JSONArray jsonArray = new JSONArray(); - jsonArray.put((Object)null); - - StringWriter writer = new StringWriter(); - String output = jsonArray.write(writer).toString(); - assertTrue("String values should be equal", "[null]".equals(output)); - - jsonArray = new JSONArray(); - jsonArray.put(JSONObject.NULL); - writer = new StringWriter(); - output = jsonArray.write(writer).toString(); - assertTrue("String values should be equal", "[null]".equals(output)); - - jsonArray = new JSONArray(); - jsonArray.put(new JSONObject()); - writer = new StringWriter(); - output = jsonArray.write(writer).toString(); - assertTrue("String values should be equal", "[{}]".equals(output)); - - jsonArray = new JSONArray(); - jsonArray.put(new JSONArray()); - writer = new StringWriter(); - output = jsonArray.write(writer).toString(); - assertTrue("String values should be equal", "[[]]".equals(output)); - - jsonArray = new JSONArray(); - Map singleMap = Collections.singletonMap("key1", "value1"); - jsonArray.put((Object)singleMap); - writer = new StringWriter(); - output = jsonArray.write(writer).toString(); - assertTrue("String values should be equal", "[{\"key1\":\"value1\"}]".equals(output)); - - jsonArray = new JSONArray(); - List singleList = Collections.singletonList("entry1"); - jsonArray.put((Object)singleList); - writer = new StringWriter(); - output = jsonArray.write(writer).toString(); - assertTrue("String values should be equal", "[[\"entry1\"]]".equals(output)); - - jsonArray = new JSONArray(); - int[] intArray = new int[] { 1, 2, 3 }; - jsonArray.put(intArray); - writer = new StringWriter(); - output = jsonArray.write(writer).toString(); - assertTrue("String values should be equal", "[[1,2,3]]".equals(output)); - - jsonArray = new JSONArray(); - jsonArray.put(24); - writer = new StringWriter(); - output = jsonArray.write(writer).toString(); - assertTrue("String values should be equal", "[24]".equals(output)); - - jsonArray = new JSONArray(); - jsonArray.put("string value"); - writer = new StringWriter(); - output = jsonArray.write(writer).toString(); - assertTrue("String values should be equal", "[\"string value\"]".equals(output)); - - jsonArray = new JSONArray(); - jsonArray.put(true); - writer = new StringWriter(); - output = jsonArray.write(writer).toString(); - assertTrue("String values should be equal", "[true]".equals(output)); - - } - - /** - * This tests the JSONObject.valueToString() method. These should be - * identical to the values above, except for the enclosing [ and ]. - */ - @Test - public void valuesToString() throws Exception { - - String output = JSONObject.valueToString(null); - assertTrue("String values should be equal", "null".equals(output)); - - output = JSONObject.valueToString(JSONObject.NULL); - assertTrue("String values should be equal", "null".equals(output)); - - output = JSONObject.valueToString(new JSONObject()); - assertTrue("String values should be equal", "{}".equals(output)); - - output = JSONObject.valueToString(new JSONArray()); - assertTrue("String values should be equal", "[]".equals(output)); - - Map singleMap = Collections.singletonMap("key1", "value1"); - output = JSONObject.valueToString(singleMap); - assertTrue("String values should be equal", "{\"key1\":\"value1\"}".equals(output)); - - List singleList = Collections.singletonList("entry1"); - output = JSONObject.valueToString(singleList); - assertTrue("String values should be equal", "[\"entry1\"]".equals(output)); - - int[] intArray = new int[] { 1, 2, 3 }; - output = JSONObject.valueToString(intArray); - assertTrue("String values should be equal", "[1,2,3]".equals(output)); - - output = JSONObject.valueToString(24); - assertTrue("String values should be equal", "24".equals(output)); - - output = JSONObject.valueToString("string value"); - assertTrue("String values should be equal", "\"string value\"".equals(output)); - - output = JSONObject.valueToString(true); - assertTrue("String values should be equal", "true".equals(output)); - - } - - /** - * Test what happens when toJSONString() returns a well-formed JSON value. - * This is the usual case. - */ - @Test - public void testJSONStringValue() throws Exception { - JSONStringValue jsonString = new JSONStringValue(); - JSONArray jsonArray = new JSONArray(); - - jsonArray.put(jsonString); - - StringWriter writer = new StringWriter(); - String output = jsonArray.write(writer).toString(); - assertTrue("String values should be equal", "[\"the JSON string value\"]".equals(output)); - - output = JSONObject.valueToString(jsonString); - assertTrue("String values should be equal", "\"the JSON string value\"".equals(output)); - } - - /** - * Test what happens when toJSONString() returns null. In one case, - * use the object's toString() method. In the other, throw a JSONException. - */ - @Test - public void testJSONNullStringValue() throws Exception { - JSONNullStringValue jsonString = new JSONNullStringValue(); - JSONArray jsonArray = new JSONArray(); - - jsonArray.put(jsonString); - - StringWriter writer = new StringWriter(); - String output = jsonArray.write(writer).toString(); - assertTrue("String values should be equal", "[\"the toString value\"]".equals(output)); - - // The only different between writeValue() and valueToString(): - // in this case, valueToString throws a JSONException - try { - output = JSONObject.valueToString(jsonString); - fail("Expected an exception, got a String value"); - } catch (Exception e) { - assertTrue("Expected JSONException", e instanceof JSONException); - assertTrue("Exception message does not match", "Bad value from toJSONString: null".equals(e.getMessage())); - } - } - - /** - * Test what happens when toJSONString() returns an exception. In both - * cases, a JSONException is thrown, with the cause and message set from - * the original exception. - */ - @Test - public void testJSONStringExceptionValue() throws Exception { - JSONStringExceptionValue jsonString = new JSONStringExceptionValue(); - JSONArray jsonArray = new JSONArray(); - - jsonArray.put(jsonString); - - StringWriter writer = new StringWriter(); - String output = null; - try { - output = jsonArray.write(writer).toString(); - fail("Expected an exception, got a String value"); - } catch (Exception e) { - assertTrue("Expected JSONException", e instanceof JSONException); - assertTrue("Exception message does not match", "the exception value".equals(e.getMessage())); - } - - try { - output = JSONObject.valueToString(jsonString); - fail("Expected an exception, got a String value"); - } catch (Exception e) { - assertTrue("Expected JSONException", e instanceof JSONException); - assertTrue("Exception message does not match", "the exception value".equals(e.getMessage())); - } - } - - /** - * Test what happens when a Java object's toString() returns a String value. - * This is the usual case. - */ - @Test - public void testStringValue() throws Exception { - StringValue nonJsonString = new StringValue(); - JSONArray jsonArray = new JSONArray(); - - jsonArray.put(nonJsonString); - - StringWriter writer = new StringWriter(); - String output = jsonArray.write(writer).toString(); - assertTrue("String values should be equal", "[\"the toString value for StringValue\"]".equals(output)); - - output = JSONObject.valueToString(nonJsonString); - assertTrue("String values should be equal", "\"the toString value for StringValue\"".equals(output)); - } - - /** - * Test what happens when a Java object's toString() returns null. - * Defaults to empty string. - */ - @Test - public void testNullStringValue() throws Exception { - NullStringValue nonJsonString = new NullStringValue(); - JSONArray jsonArray = new JSONArray(); - - jsonArray.put(nonJsonString); - - StringWriter writer = new StringWriter(); - String output = jsonArray.write(writer).toString(); - assertTrue("String values should be equal", "[\"\"]".equals(output)); - - output = JSONObject.valueToString(nonJsonString); - assertTrue("String values should be equal", "\"\"".equals(output)); - } - - /** - * A JSONString that returns a valid JSON string value. - */ - private static final class JSONStringValue implements JSONString { - - @Override - public String toJSONString() { - return "\"the JSON string value\""; - } - - @Override - public String toString() { - return "the toString value for JSONStringValue"; - } - } - - /** - * A JSONString that returns null when calling toJSONString(). - */ - private static final class JSONNullStringValue implements JSONString { - - @Override - public String toJSONString() { - return null; - } - - @Override - public String toString() { - return "the toString value"; - } - } - - /** - * A JSONString that throw an exception when calling toJSONString(). - */ - private static final class JSONStringExceptionValue implements JSONString { - - @Override - public String toJSONString() { - throw new IllegalStateException("the exception value"); - } - - @Override - public String toString() { - return "the toString value for JSONStringExceptionValue"; - } - } - - public static final class StringValue { - - @Override - public String toString() { - return "the toString value for StringValue"; - } - } - - public static final class NullStringValue { - - @Override - public String toString() { - return null; - } - } -} +package org.json.junit; + +import static org.junit.Assert.*; + +import java.io.IOException; +import java.io.StringWriter; +import java.util.*; + +import org.json.*; +import org.junit.Test; + +/** + * Tests for JSONString implementations, and the difference between + * {@link JSONObject#valueToString} and {@link JSONObject#writeValue}. + */ +public class JSONStringTest { + + /** + * This tests the JSONObject.writeValue() method. We can't test directly + * due to it being a package-protected method. Instead, we can call + * JSONArray.write(), which delegates the writing of each entry to + * writeValue(). + */ + @Test + public void writeValues() throws Exception { + JSONArray jsonArray = new JSONArray(); + jsonArray.put((Object)null); + + StringWriter writer = new StringWriter(); + try { + String output = jsonArray.write(writer).toString(); + assertTrue("String values should be equal", "[null]".equals(output)); + + jsonArray = new JSONArray(); + jsonArray.put(JSONObject.NULL); + } finally { + writer.close(); + } + writer = new StringWriter(); + try { + String output = jsonArray.write(writer).toString(); + assertTrue("String values should be equal", "[null]".equals(output)); + + jsonArray = new JSONArray(); + jsonArray.put(new JSONObject()); + } finally { + writer.close(); + } + writer = new StringWriter(); + try { + String output = jsonArray.write(writer).toString(); + assertTrue("String values should be equal", "[{}]".equals(output)); + + jsonArray = new JSONArray(); + jsonArray.put(new JSONArray()); + } finally { + writer.close(); + } + writer = new StringWriter(); + try { + String output = jsonArray.write(writer).toString(); + assertTrue("String values should be equal", "[[]]".equals(output)); + + jsonArray = new JSONArray(); + Map singleMap = Collections.singletonMap("key1", "value1"); + jsonArray.put((Object)singleMap); + } finally { + writer.close(); + } + writer = new StringWriter(); + try { + String output = jsonArray.write(writer).toString(); + assertTrue("String values should be equal", "[{\"key1\":\"value1\"}]".equals(output)); + + jsonArray = new JSONArray(); + List singleList = Collections.singletonList("entry1"); + jsonArray.put((Object)singleList); + } finally { + writer.close(); + } + writer = new StringWriter(); + try { + String output = jsonArray.write(writer).toString(); + assertTrue("String values should be equal", "[[\"entry1\"]]".equals(output)); + + jsonArray = new JSONArray(); + int[] intArray = new int[] { 1, 2, 3 }; + jsonArray.put(intArray); + } finally { + writer.close(); + } + writer = new StringWriter(); + try { + String output = jsonArray.write(writer).toString(); + assertTrue("String values should be equal", "[[1,2,3]]".equals(output)); + + jsonArray = new JSONArray(); + jsonArray.put(24); + } finally { + writer.close(); + } + writer = new StringWriter(); + try { + String output = jsonArray.write(writer).toString(); + assertTrue("String values should be equal", "[24]".equals(output)); + + jsonArray = new JSONArray(); + jsonArray.put("string value"); + } finally { + writer.close(); + } + writer = new StringWriter(); + try { + String output = jsonArray.write(writer).toString(); + assertTrue("String values should be equal", "[\"string value\"]".equals(output)); + + jsonArray = new JSONArray(); + jsonArray.put(true); + } finally { + writer.close(); + } + writer = new StringWriter(); + try { + String output = jsonArray.write(writer).toString(); + assertTrue("String values should be equal", "[true]".equals(output)); + } finally { + writer.close(); + } + + } + + /** + * This tests the JSONObject.valueToString() method. These should be + * identical to the values above, except for the enclosing [ and ]. + */ + @SuppressWarnings("boxing") + @Test + public void valuesToString() throws Exception { + + String output = JSONObject.valueToString(null); + assertTrue("String values should be equal", "null".equals(output)); + + output = JSONObject.valueToString(JSONObject.NULL); + assertTrue("String values should be equal", "null".equals(output)); + + output = JSONObject.valueToString(new JSONObject()); + assertTrue("String values should be equal", "{}".equals(output)); + + output = JSONObject.valueToString(new JSONArray()); + assertTrue("String values should be equal", "[]".equals(output)); + + Map singleMap = Collections.singletonMap("key1", "value1"); + output = JSONObject.valueToString(singleMap); + assertTrue("String values should be equal", "{\"key1\":\"value1\"}".equals(output)); + + List singleList = Collections.singletonList("entry1"); + output = JSONObject.valueToString(singleList); + assertTrue("String values should be equal", "[\"entry1\"]".equals(output)); + + int[] intArray = new int[] { 1, 2, 3 }; + output = JSONObject.valueToString(intArray); + assertTrue("String values should be equal", "[1,2,3]".equals(output)); + + output = JSONObject.valueToString(24); + assertTrue("String values should be equal", "24".equals(output)); + + output = JSONObject.valueToString("string value"); + assertTrue("String values should be equal", "\"string value\"".equals(output)); + + output = JSONObject.valueToString(true); + assertTrue("String values should be equal", "true".equals(output)); + + } + + /** + * Test what happens when toJSONString() returns a well-formed JSON value. + * This is the usual case. + */ + @Test + public void testJSONStringValue() throws Exception { + JSONStringValue jsonString = new JSONStringValue(); + JSONArray jsonArray = new JSONArray(); + + jsonArray.put(jsonString); + + StringWriter writer = new StringWriter(); + try { + String output = jsonArray.write(writer).toString(); + assertTrue("String values should be equal", "[\"the JSON string value\"]".equals(output)); + + output = JSONObject.valueToString(jsonString); + assertTrue("String values should be equal", "\"the JSON string value\"".equals(output)); + } finally { + writer.close(); + } + } + + /** + * Test what happens when toJSONString() returns null. In one case, + * use the object's toString() method. In the other, throw a JSONException. + */ + @Test + public void testJSONNullStringValue() throws Exception { + JSONNullStringValue jsonString = new JSONNullStringValue(); + JSONArray jsonArray = new JSONArray(); + + jsonArray.put(jsonString); + + StringWriter writer = new StringWriter(); + try { + String output = jsonArray.write(writer).toString(); + assertTrue("String values should be equal", "[\"the toString value\"]".equals(output)); + + // The only different between writeValue() and valueToString(): + // in this case, valueToString throws a JSONException + try { + output = JSONObject.valueToString(jsonString); + fail("Expected an exception, got a String value"); + } catch (Exception e) { + assertTrue("Expected JSONException", e instanceof JSONException); + assertTrue("Exception message does not match", "Bad value from toJSONString: null".equals(e.getMessage())); + } + } finally { + writer.close(); + } + } + + /** + * Test what happens when toJSONString() returns an exception. In both + * cases, a JSONException is thrown, with the cause and message set from + * the original exception. + */ + @Test + public void testJSONStringExceptionValue() throws IOException { + JSONStringExceptionValue jsonString = new JSONStringExceptionValue(); + JSONArray jsonArray = new JSONArray(); + + jsonArray.put(jsonString); + + StringWriter writer = new StringWriter(); + try { + jsonArray.write(writer).toString(); + fail("Expected an exception, got a String value"); + } catch (JSONException e) { + assertTrue("Exception message does not match", "the exception value".equals(e.getMessage())); + } catch(Exception e) { + fail("Expected JSONException"); + } finally { + writer.close(); + } + + try { + JSONObject.valueToString(jsonString); + fail("Expected an exception, got a String value"); + } catch (JSONException e) { + assertTrue("Exception message does not match", "the exception value".equals(e.getMessage())); + } catch(Exception e) { + fail("Expected JSONException"); + } + } + + /** + * Test what happens when a Java object's toString() returns a String value. + * This is the usual case. + */ + @Test + public void testStringValue() throws Exception { + StringValue nonJsonString = new StringValue(); + JSONArray jsonArray = new JSONArray(); + + jsonArray.put(nonJsonString); + + StringWriter writer = new StringWriter(); + try { + String output = jsonArray.write(writer).toString(); + assertTrue("String values should be equal", "[\"the toString value for StringValue\"]".equals(output)); + + output = JSONObject.valueToString(nonJsonString); + assertTrue("String values should be equal", "\"the toString value for StringValue\"".equals(output)); + } finally { + writer.close(); + } + } + + /** + * Test what happens when a Java object's toString() returns null. + * Defaults to empty string. + */ + @Test + public void testNullStringValue() throws Exception { + NullStringValue nonJsonString = new NullStringValue(); + JSONArray jsonArray = new JSONArray(); + + jsonArray.put(nonJsonString); + + StringWriter writer = new StringWriter(); + try { + String output = jsonArray.write(writer).toString(); + assertTrue("String values should be equal", "[\"\"]".equals(output)); + + output = JSONObject.valueToString(nonJsonString); + assertTrue("String values should be equal", "\"\"".equals(output)); + } finally { + writer.close(); + } + } + + /** + * A JSONString that returns a valid JSON string value. + */ + private static final class JSONStringValue implements JSONString { + + @Override + public String toJSONString() { + return "\"the JSON string value\""; + } + + @Override + public String toString() { + return "the toString value for JSONStringValue"; + } + } + + /** + * A JSONString that returns null when calling toJSONString(). + */ + private static final class JSONNullStringValue implements JSONString { + + @Override + public String toJSONString() { + return null; + } + + @Override + public String toString() { + return "the toString value"; + } + } + + /** + * A JSONString that throw an exception when calling toJSONString(). + */ + private static final class JSONStringExceptionValue implements JSONString { + + @Override + public String toJSONString() { + throw new IllegalStateException("the exception value"); + } + + @Override + public String toString() { + return "the toString value for JSONStringExceptionValue"; + } + } + + public static final class StringValue { + + @Override + public String toString() { + return "the toString value for StringValue"; + } + } + + public static final class NullStringValue { + + @Override + public String toString() { + return null; + } + } +} diff --git a/src/test/java/org/json/junit/MyEnumField.java b/src/test/java/org/json/junit/MyEnumField.java index 8f2c633..f0833ef 100644 --- a/src/test/java/org/json/junit/MyEnumField.java +++ b/src/test/java/org/json/junit/MyEnumField.java @@ -3,6 +3,7 @@ package org.json.junit; /** * An enum that contains getters and some internal fields */ +@SuppressWarnings("boxing") public enum MyEnumField { VAL1(1, "val 1"), VAL2(2, "val 2"), @@ -15,12 +16,13 @@ public enum MyEnumField { this.intVal = intVal; } public String getValue() { - return value; + return this.value; } public Integer getIntVal() { - return intVal; + return this.intVal; } + @Override public String toString(){ - return value; + return this.value; } } diff --git a/src/test/java/org/json/junit/MyPublicClass.java b/src/test/java/org/json/junit/MyPublicClass.java index 1f55e3e..e483d4c 100644 --- a/src/test/java/org/json/junit/MyPublicClass.java +++ b/src/test/java/org/json/junit/MyPublicClass.java @@ -3,6 +3,7 @@ package org.json.junit; /** * Need a class with some public data members for testing */ +@SuppressWarnings("boxing") public class MyPublicClass { public Integer publicInt = 42; public String publicString = "abc"; diff --git a/src/test/java/org/json/junit/StringsResourceBundle.java b/src/test/java/org/json/junit/StringsResourceBundle.java index 83d9322..d04aeaf 100644 --- a/src/test/java/org/json/junit/StringsResourceBundle.java +++ b/src/test/java/org/json/junit/StringsResourceBundle.java @@ -6,6 +6,7 @@ import java.util.*; * A resource bundle class */ public class StringsResourceBundle extends ListResourceBundle { + @Override public Object[][] getContents() { return contents; } diff --git a/src/test/java/org/json/junit/XMLTest.java b/src/test/java/org/json/junit/XMLTest.java index 2f3fea7..dd827bd 100644 --- a/src/test/java/org/json/junit/XMLTest.java +++ b/src/test/java/org/json/junit/XMLTest.java @@ -4,11 +4,8 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; -import java.io.IOException; - import org.json.JSONArray; import org.json.JSONException; -import org.json.JSONML; import org.json.JSONObject; import org.json.XML; import org.junit.Rule;