diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9e7b59c --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +/bin/ +build +.classpath +.project +.settings/ diff --git a/src/test/org/json/junit/JSONObjectTest.java b/src/test/org/json/junit/JSONObjectTest.java index 994c4b3..dc74459 100644 --- a/src/test/org/json/junit/JSONObjectTest.java +++ b/src/test/org/json/junit/JSONObjectTest.java @@ -10,7 +10,14 @@ import java.io.StringWriter; import java.io.Writer; import java.math.BigDecimal; import java.math.BigInteger; -import java.util.*; +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 org.json.CDL; import org.json.JSONArray; @@ -19,783 +26,29 @@ import org.json.JSONObject; import org.json.XML; import org.junit.Test; -import com.jayway.jsonpath.*; +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. + * 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); - } - - /** - * 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(JsonPath.read(doc, "$.falseKey"))); - assertTrue("expected \"nullKey\":null", null == JsonPath.read(doc, "$.nullKey")); - assertTrue("expected \"stringKey\":\"hello world!\"", "hello world!".equals(JsonPath.read(doc, "$.stringKey"))); - assertTrue("expected \"doubleKey\":-23.45e67", Double.valueOf("-23.45e67").equals(JsonPath.read(doc, "$.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(JsonPath.read(doc, "$.trueKey"))); - assertTrue("expected \"falseKey\":false", Boolean.FALSE.equals(JsonPath.read(doc, "$.falseKey"))); - assertTrue("expected \"stringKey\":\"hello world!\"", "hello world!".equals(JsonPath.read(doc, "$.stringKey"))); - assertTrue("expected \"escapeStringKey\":\"h\be\tllo w\u1234orld!\"", "h\be\tllo w\u1234orld!".equals(JsonPath.read(doc,"$.escapeStringKey"))); - assertTrue("expected \"doubleKey\":-23.45e67", Double.valueOf("-23.45e67").equals(JsonPath.read(doc, "$.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)); - } - - /** - * 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 \"key2\":java.lang.Exception","java.lang.Exception".equals(JsonPath.read(doc, "$.key2"))); - assertTrue("expected 0 key1 items", ((Map)(JsonPath.read(doc, "$.key1"))).size() == 0); - } - - /** - * 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(JsonPath.read(doc, "$.trueKey"))); - assertTrue("expected \"falseKey\":false", Boolean.FALSE.equals(JsonPath.read(doc, "$.falseKey"))); - assertTrue("expected \"stringKey\":\"hello world!\"", "hello world!".equals(JsonPath.read(doc, "$.stringKey"))); - assertTrue("expected \"escapeStringKey\":\"h\be\tllo w\u1234orld!\"", "h\be\tllo w\u1234orld!".equals(JsonPath.read(doc,"$.escapeStringKey"))); - assertTrue("expected \"intKey\":42", Integer.valueOf("42").equals(JsonPath.read(doc, "$.intKey"))); - assertTrue("expected \"doubleKey\":-23.45e67", Double.valueOf("-23.45e67").equals(JsonPath.read(doc, "$.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 true", Boolean.TRUE.equals(JsonPath.read(doc, "$.trueKey"))); - assertTrue("expected false", Boolean.FALSE.equals(JsonPath.read(doc, "$.falseKey"))); - assertTrue("expected hello world!","hello world!".equals(JsonPath.read(doc, "$.stringKey"))); - assertTrue("expected h\be\tllo w\u1234orld!", "h\be\tllo w\u1234orld!".equals(JsonPath.read(doc,"$.escapeStringKey"))); - assertTrue("expected 42", Integer.valueOf("42").equals(JsonPath.read(doc, "$.intKey"))); - assertTrue("expected -23.45e7", Double.valueOf("-23.45e7").equals(JsonPath.read(doc, "$.doubleKey"))); - assertTrue("expected 0 items in stringReaderKey", ((Map) (JsonPath.read(doc, "$.stringReaderKey"))).size() == 0); - // 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(JsonPath.read(doc, "$.publicString"))); - assertTrue("expected \"publicInt\":42", Integer.valueOf(42).equals(JsonPath.read(doc, "$.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(JsonPath.read(doc, "$.greetings.hello"))); - assertTrue("expected \"world\":\"World!\"", "World!".equals(JsonPath.read(doc, "$.greetings.world"))); - assertTrue("expected 2 farewells items", ((Map)(JsonPath.read(doc, "$.farewells"))).size() == 2); - assertTrue("expected \"later\":\"Later, \"", "Later, ".equals(JsonPath.read(doc, "$.farewells.later"))); - assertTrue("expected \"world\":\"World!\"", "Alligator!".equals(JsonPath.read(doc, "$.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(JsonPath.read(doc, "$.myArray[0]"))); - assertTrue("expected false", Boolean.FALSE.equals(JsonPath.read(doc, "$.myArray[1]"))); - assertTrue("expected hello world!", "hello world!".equals(JsonPath.read(doc, "$.myArray[2]"))); - assertTrue("expected h\be\tllo w\u1234orld!", "h\be\tllo w\u1234orld!".equals(JsonPath.read(doc,"$.myArray[3]"))); - assertTrue("expected 42", Integer.valueOf(42).equals(JsonPath.read(doc, "$.myArray[4]"))); - assertTrue("expected -23.45e7", Double.valueOf(-23.45e7).equals(JsonPath.read(doc, "$.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(JsonPath.read(doc, "$.myArray[0]"))); - assertTrue("expected false", Boolean.FALSE.equals(JsonPath.read(doc, "$.myArray[1]"))); - assertTrue("expected hello world!", "hello world!".equals(JsonPath.read(doc, "$.myArray[2]"))); - assertTrue("expected h\be\tllo w\u1234orld!", "h\be\tllo w\u1234orld!".equals(JsonPath.read(doc,"$.myArray[3]"))); - assertTrue("expected 42", Integer.valueOf(42).equals(JsonPath.read(doc, "$.myArray[4]"))); - assertTrue("expected -23.45e7", Double.valueOf(-23.45e7).equals(JsonPath.read(doc, "$.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.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 + * 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. + * 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"); + final BigInteger bigInteger = new BigInteger( + "123456789012345678901234567890"); JSONObject jsonObject = new JSONObject(bigInteger); Object obj = jsonObject.get("lowestSetBit"); assertTrue("JSONObject only has 1 value", jsonObject.length() == 1); @@ -805,18 +58,18 @@ public class JSONObjectTest { 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. + * 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( + final 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 put(String, Object) method stores and serializes bigInt + * and bigDec correctly. Nothing needs to change. */ jsonObject = new JSONObject(); jsonObject.put("bigInt", bigInteger); @@ -824,21 +77,29 @@ public class JSONObjectTest { 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}")); + 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}")); + 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 @@ -846,18 +107,21 @@ public class JSONObjectTest { try { jsonObject.getBigDecimal("bigInt"); assertTrue("expected an exeption", false); - } catch (JSONException ignored) {} + } catch (final 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"); + } catch (final JSONException ignored) { + } + jsonObject.put("stringKey", "abc"); try { jsonObject.getBigDecimal("stringKey"); assertTrue("expected an exeption", false); - } catch (JSONException ignored) {} + } catch (final JSONException ignored) { + } obj = jsonObject.optBigInteger("bigDec", BigInteger.ONE); assertTrue("expected BigInteger", obj.equals(BigInteger.ONE)); @@ -868,117 +132,730 @@ public class JSONObjectTest { assertTrue("numberToString() handles bigInteger correctly", str.equals("123456789012345678901234567890")); str = JSONObject.numberToString(bigDecimal); - assertTrue("numberToString() handles bigDecimal correctly", + 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. + * 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())); + assertTrue("stringToValue() changes bigDecimal string", !obj.toString() + .equals(bigDecimal.toString())); /** * wrap() vs put() big number behavior is now the same. */ - // bigInt map ctor + // 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}")); + 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}")); + 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}")); + 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 + 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)); + 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) {} + } catch (final 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)); + } catch (final 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(); + final 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]")); + final 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")); + 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 + // 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")); + 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")); + // 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)); + assertTrue("wrap() returns big num", obj.equals(bigInteger)); obj = JSONObject.wrap(bigDecimal); - assertTrue("wrap() returns string",obj.equals(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) + * A JSONObject can be created with no content + */ + @Test + public void emptyJsonObject() { + final JSONObject jsonObject = new JSONObject(); + assertTrue("jsonObject should be empty", jsonObject.length() == 0); + } + + /** + * Populate a JSONArray from an empty JSONObject names() method. It should + * be empty. + */ + @Test + public void emptyJsonObjectNamesToJsonAray() { + final JSONObject jsonObject = new JSONObject(); + final JSONArray jsonArray = jsonObject.names(); + assertTrue("jsonArray should be null", jsonArray == null); + } + + /** + * Exercise the JSONObject equals() method + */ + @Test + public void equals() { + final String str = "{\"key\":\"value\"}"; + final JSONObject aJsonObject = new JSONObject(str); + assertTrue("Same JSONObject should be equal to itself", + aJsonObject.equals(aJsonObject)); + } + + /** + * This test documents how JSON-Java handles invalid numeric input. + */ + @Test + public void jsonInvalidNumberValues() { + // Number-notations supported by Java and invalid as JSON + final 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" + "}"; + final 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))); + } + + /** + * Exercise the JSONObject.accumulate() method + */ + @Test + public void jsonObjectAccumulate() { + + final 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 (final JSONException ignored) { + } + + // validate JSON + final 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(JsonPath.read(doc, "$.myArray[0]"))); + assertTrue("expected false", + Boolean.FALSE.equals(JsonPath.read(doc, "$.myArray[1]"))); + assertTrue("expected hello world!", + "hello world!".equals(JsonPath.read(doc, "$.myArray[2]"))); + assertTrue("expected h\be\tllo w\u1234orld!", + "h\be\tllo w\u1234orld!".equals(JsonPath.read(doc, + "$.myArray[3]"))); + assertTrue("expected 42", + Integer.valueOf(42).equals(JsonPath.read(doc, "$.myArray[4]"))); + assertTrue( + "expected -23.45e7", + Double.valueOf(-23.45e7).equals( + JsonPath.read(doc, "$.myArray[5]"))); + } + + /** + * Exercise the JSONObject append() functionality + */ + @Test + public void jsonObjectAppend() { + final 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 (final JSONException ignored) { + } + + // validate JSON + final 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(JsonPath.read(doc, "$.myArray[0]"))); + assertTrue("expected false", + Boolean.FALSE.equals(JsonPath.read(doc, "$.myArray[1]"))); + assertTrue("expected hello world!", + "hello world!".equals(JsonPath.read(doc, "$.myArray[2]"))); + assertTrue("expected h\be\tllo w\u1234orld!", + "h\be\tllo w\u1234orld!".equals(JsonPath.read(doc, + "$.myArray[3]"))); + assertTrue("expected 42", + Integer.valueOf(42).equals(JsonPath.read(doc, "$.myArray[4]"))); + assertTrue( + "expected -23.45e7", + Double.valueOf(-23.45e7).equals( + JsonPath.read(doc, "$.myArray[5]"))); + } + + /** + * 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. + */ + final 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("") { + }); + + final JSONObject jsonObject = new JSONObject(myBean); + + // validate JSON + final Object doc = Configuration.defaultConfiguration().jsonProvider() + .parse(jsonObject.toString()); + assertTrue("expected 8 top level items", + ((Map) JsonPath.read(doc, "$")).size() == 8); + assertTrue("expected true", + Boolean.TRUE.equals(JsonPath.read(doc, "$.trueKey"))); + assertTrue("expected false", + Boolean.FALSE.equals(JsonPath.read(doc, "$.falseKey"))); + assertTrue("expected hello world!", + "hello world!".equals(JsonPath.read(doc, "$.stringKey"))); + assertTrue("expected h\be\tllo w\u1234orld!", + "h\be\tllo w\u1234orld!".equals(JsonPath.read(doc, + "$.escapeStringKey"))); + assertTrue("expected 42", + Integer.valueOf("42").equals(JsonPath.read(doc, "$.intKey"))); + assertTrue( + "expected -23.45e7", + Double.valueOf("-23.45e7").equals( + JsonPath.read(doc, "$.doubleKey"))); + assertTrue( + "expected 0 items in stringReaderKey", + ((Map) JsonPath.read(doc, "$.stringReaderKey")).size() == 0); + // 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); + } + + /** + * JSONObjects can be built from a Map. In this test all of + * the map entries are valid JSON types. + */ + @Test + public void jsonObjectByMap() { + final 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)); + final JSONObject jsonObject = new JSONObject(map); + + // validate JSON + final 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(JsonPath.read(doc, "$.trueKey"))); + assertTrue("expected \"falseKey\":false", + Boolean.FALSE.equals(JsonPath.read(doc, "$.falseKey"))); + assertTrue("expected \"stringKey\":\"hello world!\"", + "hello world!".equals(JsonPath.read(doc, "$.stringKey"))); + assertTrue("expected \"escapeStringKey\":\"h\be\tllo w\u1234orld!\"", + "h\be\tllo w\u1234orld!".equals(JsonPath.read(doc, + "$.escapeStringKey"))); + assertTrue( + "expected \"doubleKey\":-23.45e67", + Double.valueOf("-23.45e67").equals( + JsonPath.read(doc, "$.doubleKey"))); + } + + /** + * JSONObjects can be built from a Map. In this test one of + * the map values is null + */ + @Test + public void jsonObjectByMapWithNullValue() { + final 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)); + final JSONObject jsonObject = new JSONObject(map); + + // validate JSON + final 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(JsonPath.read(doc, "$.trueKey"))); + assertTrue("expected \"falseKey\":false", + Boolean.FALSE.equals(JsonPath.read(doc, "$.falseKey"))); + assertTrue("expected \"stringKey\":\"hello world!\"", + "hello world!".equals(JsonPath.read(doc, "$.stringKey"))); + assertTrue("expected \"escapeStringKey\":\"h\be\tllo w\u1234orld!\"", + "h\be\tllo w\u1234orld!".equals(JsonPath.read(doc, + "$.escapeStringKey"))); + assertTrue("expected \"intKey\":42", + Integer.valueOf("42").equals(JsonPath.read(doc, "$.intKey"))); + assertTrue( + "expected \"doubleKey\":-23.45e67", + Double.valueOf("-23.45e67").equals( + JsonPath.read(doc, "$.doubleKey"))); + } + + /** + * 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() { + final Map jsonMap = new HashMap(); + // Just insert some random objects + jsonMap.put("key1", new CDL()); + jsonMap.put("key2", new Exception()); + + final JSONObject jsonObject = new JSONObject(jsonMap); + + // validate JSON + final Object doc = Configuration.defaultConfiguration().jsonProvider() + .parse(jsonObject.toString()); + assertTrue("expected 2 top level items", + ((Map) JsonPath.read(doc, "$")).size() == 2); + assertTrue("expected \"key2\":java.lang.Exception", + "java.lang.Exception".equals(JsonPath.read(doc, "$.key2"))); + assertTrue("expected 0 key1 items", + ((Map) JsonPath.read(doc, "$.key1")).size() == 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() { + final String str = "{" + "\"trueKey\":true," + "\"falseKey\":false," + + "\"nullKey\":null," + "\"stringKey\":\"hello world!\"," + + "\"escapeStringKey\":\"h\be\tllo w\u1234orld!\"," + + "\"intKey\":42," + "\"doubleKey\":-23.45e67" + "}"; + final String[] keys = { "falseKey", "stringKey", "nullKey", "doubleKey" }; + final JSONObject jsonObject = new JSONObject(str); + + // validate JSON + final JSONObject jsonObjectByName = new JSONObject(jsonObject, keys); + final 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(JsonPath.read(doc, "$.falseKey"))); + assertTrue("expected \"nullKey\":null", + null == JsonPath.read(doc, "$.nullKey")); + assertTrue("expected \"stringKey\":\"hello world!\"", + "hello world!".equals(JsonPath.read(doc, "$.stringKey"))); + assertTrue( + "expected \"doubleKey\":-23.45e67", + Double.valueOf("-23.45e67").equals( + JsonPath.read(doc, "$.doubleKey"))); + } + + /** + * 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() { + final MyBean myBean = null; + new JSONObject(myBean); + } + + /** + * 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() { + final Map map = null; + final JSONObject jsonObject = new JSONObject(map); + assertTrue("jsonObject should be empty", jsonObject.length() == 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() { + final String[] keys = { "publicString", "publicInt" }; + // just need a class that has public data members + final MyPublicClass myPublicClass = new MyPublicClass(); + final JSONObject jsonObject = new JSONObject(myPublicClass, keys); + + // validate JSON + final 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(JsonPath.read(doc, "$.publicString"))); + assertTrue("expected \"publicInt\":42", + Integer.valueOf(42).equals(JsonPath.read(doc, "$.publicInt"))); + } + + /** + * Exercise the JSONObject from resource bundle functionality. The test + * resource bundle is uncomplicated, but provides adequate test coverage. + */ + @Test + public void jsonObjectByResourceBundle() { + final JSONObject jsonObject = new JSONObject( + "org.json.junit.StringsResourceBundle", Locale.getDefault()); + + // validate JSON + final 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(JsonPath.read(doc, "$.greetings.hello"))); + assertTrue("expected \"world\":\"World!\"", + "World!".equals(JsonPath.read(doc, "$.greetings.world"))); + assertTrue("expected 2 farewells items", + ((Map) JsonPath.read(doc, "$.farewells")).size() == 2); + assertTrue("expected \"later\":\"Later, \"", + "Later, ".equals(JsonPath.read(doc, "$.farewells.later"))); + assertTrue("expected \"world\":\"World!\"", + "Alligator!".equals(JsonPath.read(doc, "$.farewells.gator"))); + } + + /** + * Exercise the JSONObject doubleToString() method + */ + @Test + public void jsonObjectDoubleToString() { + final String[] expectedStrs = { "1", "1", "-23.4", "-2.345E68", "null", + "null" }; + final Double[] doubles = { 1.0, 00001.00000, -23.4, -23.45e67, + Double.NaN, Double.NEGATIVE_INFINITY }; + for (int i = 0; i < expectedStrs.length; ++i) { + final String actualStr = JSONObject.doubleToString(doubles[i]); + assertTrue("value expected [" + expectedStrs[i] + "] found [" + + actualStr + "]", expectedStrs[i].equals(actualStr)); + } + } + + /** + * Exercise the JSONObject increment() method. + */ + @Test + public void jsonObjectIncrement() { + final String str = "{" + "\"keyLong\":9999999991," + + "\"keyDouble\":1.1" + "}"; + final 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 + final 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(JsonPath.read(doc, "$.keyInt"))); + assertTrue( + "expected 9999999993", + Long.valueOf(9999999993L).equals( + JsonPath.read(doc, "$.keyLong"))); + assertTrue("expected 3.1", + Double.valueOf(3.1).equals(JsonPath.read(doc, "$.keyDouble"))); + assertTrue("expected 123456789123456789123456789123456781", + new BigInteger("123456789123456789123456789123456781") + .equals(JsonPath.read(doc, "$.keyBigInt"))); + assertTrue("expected 123456789123456789123456789123456781.1", + new BigDecimal("123456789123456789123456789123456781.1") + .equals(JsonPath.read(doc, "$.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( + JsonPath.read(doc, "$.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!", + 0.2f == 0.2d); + assertFalse("Document unexpected behaviour with implicit type-cast!", + 0.2f == 0.2d); + final Double d1 = new Double(1.1f); + final 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 + final 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))); + + final 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.) + + } + + /** + * 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 @@ -986,32 +863,27 @@ public class JSONObjectTest { JSONObject jsonObject; // getNames() from null JSONObject - assertTrue("null names from null Object", - null == JSONObject.getNames((Object)null)); + 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", + 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); + String[] names = JSONObject.getNames(jsonObject); assertTrue("names should be null", names == null); - // getNames() from empty JSONObject - String emptyStr = "{}"; + final 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!\","+ - "}"; + final String str = "{" + "\"trueKey\":true," + "\"falseKey\":false," + + "\"stringKey\":\"hello world!\"," + "}"; jsonObject = new JSONObject(str); names = JSONObject.getNames(jsonObject); JSONArray jsonArray = new JSONArray(names); @@ -1032,10 +904,10 @@ public class JSONObjectTest { ((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 + * getNames() from an enum with properties has an interesting result. It + * returns the enum values, not the selected enum properties */ - MyEnumField myEnumField = MyEnumField.VAL1; + final MyEnumField myEnumField = MyEnumField.VAL1; names = JSONObject.getNames(myEnumField); // validate JSON @@ -1044,22 +916,18 @@ public class JSONObjectTest { .parse(jsonArray.toString()); docList = JsonPath.read(doc, "$"); assertTrue("expected 3 items", docList.size() == 3); - assertTrue( - "expected to find VAL1", + assertTrue("expected to find VAL1", ((List) JsonPath.read(doc, "$[?(@=='VAL1')]")).size() == 1); - assertTrue( - "expected to find VAL2", + assertTrue("expected to find VAL2", ((List) JsonPath.read(doc, "$[?(@=='VAL2')]")).size() == 1); - assertTrue( - "expected to find VAL3", + 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. + * 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(); + final MyPublicClass myPublicClass = new MyPublicClass(); names = JSONObject.getNames(myPublicClass); // validate JSON @@ -1068,165 +936,242 @@ public class JSONObjectTest { .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 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. + * 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!\","+ - "}"; + final String str = "{" + "\"trueKey\":true," + "\"falseKey\":false," + + "\"stringKey\":\"hello world!\"," + "}"; - JSONObject jsonObject = new JSONObject(str); - JSONArray jsonArray = jsonObject.names(); + final JSONObject jsonObject = new JSONObject(str); + final 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); + final 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. + * Tests how JSONObject get[type] handles incorrect types */ @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(JsonPath.read(doc, "$.keyInt"))); - assertTrue("expected 9999999993", Long.valueOf(9999999993L).equals(JsonPath.read(doc, "$.keyLong"))); - assertTrue("expected 3.1", Double.valueOf(3.1).equals(JsonPath.read(doc, "$.keyDouble"))); - assertTrue("expected 123456789123456789123456789123456781", new BigInteger("123456789123456789123456789123456781").equals(JsonPath.read(doc, "$.keyBigInt"))); - assertTrue("expected 123456789123456789123456789123456781.1", new BigDecimal("123456789123456789123456789123456781.1").equals(JsonPath.read(doc, "$.keyBigDec"))); + public void jsonObjectNonAndWrongValues() { + final 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\"}" + "}"; + final JSONObject jsonObject = new JSONObject(str); + try { + jsonObject.getBoolean("nonKey"); + assertTrue("Expected an exception", false); + } catch (final JSONException e) { + assertTrue("expecting an exception message", + "JSONObject[\"nonKey\"] not found.".equals(e.getMessage())); + } + try { + jsonObject.getBoolean("stringKey"); + assertTrue("Expected an exception", false); + } catch (final 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 (final JSONException e) { + assertTrue("Expecting an exception message", + "JSONObject[\"nonKey\"] not found.".equals(e.getMessage())); + } + try { + jsonObject.getString("trueKey"); + assertTrue("Expected an exception", false); + } catch (final 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 (final JSONException e) { + assertTrue("Expecting an exception message", + "JSONObject[\"nonKey\"] not found.".equals(e.getMessage())); + } + try { + jsonObject.getDouble("stringKey"); + assertTrue("Expected an exception", false); + } catch (final 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 (final JSONException e) { + assertTrue("Expecting an exception message", + "JSONObject[\"nonKey\"] not found.".equals(e.getMessage())); + } + try { + jsonObject.getInt("stringKey"); + assertTrue("Expected an exception", false); + } catch (final 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 (final JSONException e) { + assertTrue("Expecting an exception message", + "JSONObject[\"nonKey\"] not found.".equals(e.getMessage())); + } + try { + jsonObject.getLong("stringKey"); + assertTrue("Expected an exception", false); + } catch (final 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 (final JSONException e) { + assertTrue("Expecting an exception message", + "JSONObject[\"nonKey\"] not found.".equals(e.getMessage())); + } + try { + jsonObject.getJSONArray("stringKey"); + assertTrue("Expected an exception", false); + } catch (final 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 (final JSONException e) { + assertTrue("Expecting an exception message", + "JSONObject[\"nonKey\"] not found.".equals(e.getMessage())); + } + try { + jsonObject.getJSONObject("stringKey"); + assertTrue("Expected an exception", false); + } catch (final JSONException e) { + assertTrue("Expecting an exception message", + "JSONObject[\"stringKey\"] is not a JSONObject.".equals(e + .getMessage())); + } + } + /** + * JSON null is not the same as Java null. This test examines the + * differences in how they are handled by JSON-java. + */ + @Test + public void jsonObjectNullOperations() { /** - * 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(JsonPath.read(doc, "$.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 + * The Javadoc for JSONObject.NULL states: "JSONObject.NULL is + * equivalent to the value that JavaScript calls null, whilst Java's + * null is equivalent to the value that JavaScript calls undefined." + * + * Standard ECMA-262 6th Edition / June 2015 (included to help explain + * the javadoc): undefined value: primitive value used when a variable + * has not been assigned a value Undefined type: type whose sole value + * is the undefined value null value: primitive value that represents + * the intentional absence of any object value Null type: type whose + * sole value is the null value Java SE8 language spec (included to help + * explain the javadoc): The Kinds of Types and Values ... There is also + * a special null type, the type of the expression null, which has no + * name. Because the null type has no name, it is impossible to declare + * a variable of the null type or to cast to the null type. The null + * reference is the only possible value of an expression of null type. + * The null reference can always be assigned or cast to any reference + * type. In practice, the programmer can ignore the null type and just + * pretend that null is merely a special literal that can be of any + * reference type. Extensible Markup Language (XML) 1.0 Fifth Edition / + * 26 November 2008 No mention of null ECMA-404 1st Edition / October + * 2013: JSON Text ... These are three literal name tokens: ... null + * + * There seems to be no best practice to follow, it's all about what we + * want the code to do. */ + // add JSONObject.NULL then convert to string in the manner of + // XML.toString() + final JSONObject jsonObjectJONull = new JSONObject(); + Object obj = JSONObject.NULL; + jsonObjectJONull.put("key", obj); + Object value = jsonObjectJONull.opt("key"); + assertTrue("opt() JSONObject.NULL should find JSONObject.NULL", + obj.equals(value)); + value = jsonObjectJONull.get("key"); + assertTrue("get() JSONObject.NULL should find JSONObject.NULL", + obj.equals(value)); + if (value == null) { + value = ""; + } + String string = value instanceof String ? (String) value : null; + assertTrue("XML toString() should convert JSONObject.NULL to null", + string == null); + + // now try it with null + final JSONObject jsonObjectNull = new JSONObject(); + obj = null; + jsonObjectNull.put("key", obj); + value = jsonObjectNull.opt("key"); + assertTrue("opt() null should find null", value == null); + if (value == null) { + value = ""; + } + string = value instanceof String ? (String) value : null; + assertTrue("should convert null to empty string", "".equals(string)); + try { + value = jsonObjectNull.get("key"); + assertTrue("get() null should throw exception", false); + } catch (final Exception ignored) { + } + /** - * 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.) - + * XML.toString() then goes on to do something with the value if the key + * val is "content", then value.toString() will be called. This will + * evaluate to "null" for JSONObject.NULL, and the empty string for + * null. But if the key is anything else, then JSONObject.NULL will be + * emitted as null and null will be emitted as "" + */ + final String sJONull = XML.toString(jsonObjectJONull); + assertTrue("JSONObject.NULL should emit a null value", + "null".equals(sJONull)); + final String sNull = XML.toString(jsonObjectNull); + assertTrue("null should emit an empty string", "".equals(sNull)); } /** @@ -1236,449 +1181,22 @@ public class JSONObjectTest { public void jsonObjectNumberToString() { String str; Double dVal; - Integer iVal = 1; + final Integer iVal = 1; str = JSONObject.numberToString(iVal); - assertTrue("expected "+iVal+" actual "+str, iVal.toString().equals(str)); + 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)); + 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)); + 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(JsonPath.read(doc, "$.trueKey"))); - assertTrue("expected false", Boolean.FALSE.equals(JsonPath.read(doc, "$.falseKey"))); - assertTrue("expected 3 arrayKey items", ((List)(JsonPath.read(doc, "$.arrayKey"))).size() == 3); - assertTrue("expected 0", Integer.valueOf(0).equals(JsonPath.read(doc, "$.arrayKey[0]"))); - assertTrue("expected 1", Integer.valueOf(1).equals(JsonPath.read(doc, "$.arrayKey[1]"))); - assertTrue("expected 2", Integer.valueOf(2).equals(JsonPath.read(doc, "$.arrayKey[2]"))); - assertTrue("expected 4 objectKey items", ((Map)(JsonPath.read(doc, "$.objectKey"))).size() == 4); - assertTrue("expected myVal1", "myVal1".equals(JsonPath.read(doc, "$.objectKey.myKey1"))); - assertTrue("expected myVal2", "myVal2".equals(JsonPath.read(doc, "$.objectKey.myKey2"))); - assertTrue("expected myVal3", "myVal3".equals(JsonPath.read(doc, "$.objectKey.myKey3"))); - assertTrue("expected myVal4", "myVal4".equals(JsonPath.read(doc, "$.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(JsonPath.read(doc, "$.trueKey"))); - assertTrue("expected false", Boolean.FALSE.equals(JsonPath.read(doc, "$.falseKey"))); - assertTrue("expected 3 arrayKey items", ((List)(JsonPath.read(doc, "$.arrayKey"))).size() == 3); - assertTrue("expected 0", Integer.valueOf(0).equals(JsonPath.read(doc, "$.arrayKey[0]"))); - assertTrue("expected 1", Integer.valueOf(1).equals(JsonPath.read(doc, "$.arrayKey[1]"))); - assertTrue("expected 2", Integer.valueOf(2).equals(JsonPath.read(doc, "$.arrayKey[2]"))); - assertTrue("expected 4 objectKey items", ((Map)(JsonPath.read(doc, "$.objectKey"))).size() == 4); - assertTrue("expected myVal1", "myVal1".equals(JsonPath.read(doc, "$.objectKey.myKey1"))); - assertTrue("expected myVal2", "myVal2".equals(JsonPath.read(doc, "$.objectKey.myKey2"))); - assertTrue("expected myVal3", "myVal3".equals(JsonPath.read(doc, "$.objectKey.myKey3"))); - assertTrue("expected myVal4", "myVal4".equals(JsonPath.read(doc, "$.objectKey.myKey4"))); - } - - /** - * 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(JsonPath.read(doc, "$.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(JsonPath.read(doc, "$.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("jsonArra 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(JsonPath.read(doc, "$[0]"))); - assertTrue("expected 2", Integer.valueOf(2).equals(JsonPath.read(doc, "$[1]"))); - assertTrue("expected 3", Integer.valueOf(3).equals(JsonPath.read(doc, "$[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(JsonPath.read(doc, "$[0]"))); - assertTrue("expected 2", Integer.valueOf(2).equals(JsonPath.read(doc, "$[1]"))); - assertTrue("expected 3", Integer.valueOf(3).equals(JsonPath.read(doc, "$[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(JsonPath.read(doc, "$[0]"))); - assertTrue("expected 2", Integer.valueOf(2).equals(JsonPath.read(doc, "$[1]"))); - assertTrue("expected 3", Integer.valueOf(3).equals(JsonPath.read(doc, "$[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(JsonPath.read(doc, "$.key1"))); - assertTrue("expected val2", "val2".equals(JsonPath.read(doc, "$.key2"))); - assertTrue("expected val3", "val3".equals(JsonPath.read(doc, "$.key3"))); - } - - /** - * 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); + assertTrue("expected 5000000 actual " + str, str.equals("5000000")); } /** @@ -1687,8 +1205,8 @@ public class JSONObjectTest { @Test public void jsonObjectOptDefault() { - String str = "{\"myKey\": \"myval\"}"; - JSONObject jsonObject = new JSONObject(str); + final String str = "{\"myKey\": \"myval\"}"; + final JSONObject jsonObject = new JSONObject(str); assertTrue("optBoolean() should return default boolean", Boolean.TRUE == jsonObject.optBoolean("myKey", Boolean.TRUE)); @@ -1704,6 +1222,207 @@ public class JSONObjectTest { "hi".equals(jsonObject.optString("hiKey", "hi"))); } + /** + * Explore how JSONObject handles parsing errors. + */ + @Test + public void jsonObjectParsingErrors() { + try { + // does not start with '{' + final String str = "abc"; + new JSONObject(str); + assertTrue("Expected an exception", false); + } catch (final 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 '}' + final String str = "{"; + new JSONObject(str); + assertTrue("Expected an exception", false); + } catch (final 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 ':' + final String str = "{\"myKey\" = true}"; + new JSONObject(str); + assertTrue("Expected an exception", false); + } catch (final 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 + final String str = "{\"myKey\":true \"myOtherKey\":false}"; + new JSONObject(str); + assertTrue("Expected an exception", false); + } catch (final JSONException e) { + assertTrue("Expecting an exception message", + "Expected a ',' or '}' at 15 [character 16 line 1]" + .equals(e.getMessage())); + } + try { + // append to wrong key + final String str = "{\"myKey\":true, \"myOtherKey\":false}"; + final JSONObject jsonObject = new JSONObject(str); + jsonObject.append("myKey", "hello"); + assertTrue("Expected an exception", false); + } catch (final JSONException e) { + assertTrue("Expecting an exception message", + "JSONObject[myKey] is not a JSONArray.".equals(e + .getMessage())); + } + try { + // increment wrong key + final String str = "{\"myKey\":true, \"myOtherKey\":false}"; + final JSONObject jsonObject = new JSONObject(str); + jsonObject.increment("myKey"); + assertTrue("Expected an exception", false); + } catch (final JSONException e) { + assertTrue("Expecting an exception message", + "Unable to increment [\"myKey\"].".equals(e.getMessage())); + } + try { + // invalid key + final String str = "{\"myKey\":true, \"myOtherKey\":false}"; + final JSONObject jsonObject = new JSONObject(str); + jsonObject.get(null); + assertTrue("Expected an exception", false); + } catch (final 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 (final JSONException e) { + assertTrue("Expecting an exception message", + "Null pointer".equals(e.getMessage())); + } + try { + // null put key + final JSONObject jsonObject = new JSONObject("{}"); + jsonObject.put(null, 0); + assertTrue("Expected an exception", false); + } catch (final NullPointerException ignored) { + } + try { + // multiple putOnce key + final JSONObject jsonObject = new JSONObject("{}"); + jsonObject.putOnce("hello", "world"); + jsonObject.putOnce("hello", "world!"); + assertTrue("Expected an exception", false); + } catch (final JSONException e) { + assertTrue("", true); + } + try { + // test validity of invalid double + JSONObject.testValidity(Double.NaN); + assertTrue("Expected an exception", false); + } catch (final JSONException e) { + assertTrue("", true); + } + try { + // test validity of invalid float + JSONObject.testValidity(Float.NEGATIVE_INFINITY); + assertTrue("Expected an exception", false); + } catch (final JSONException e) { + assertTrue("", true); + } + } + + /** + * Exercise JSONObject put() and similar() methods + */ + @Test + public void jsonObjectPut() { + final String expectedStr = "{" + "\"trueKey\":true," + + "\"falseKey\":false," + "\"arrayKey\":[0,1,2]," + + "\"objectKey\":{" + "\"myKey1\":\"myVal1\"," + + "\"myKey2\":\"myVal2\"," + "\"myKey3\":\"myVal3\"," + + "\"myKey4\":\"myVal4\"" + "}" + "}"; + final JSONObject jsonObject = new JSONObject(); + jsonObject.put("trueKey", true); + jsonObject.put("falseKey", false); + final Integer[] intArray = { 0, 1, 2 }; + jsonObject.put("arrayKey", Arrays.asList(intArray)); + final 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 + final 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(JsonPath.read(doc, "$.trueKey"))); + assertTrue("expected false", + Boolean.FALSE.equals(JsonPath.read(doc, "$.falseKey"))); + assertTrue("expected 3 arrayKey items", + ((List) JsonPath.read(doc, "$.arrayKey")).size() == 3); + assertTrue("expected 0", + Integer.valueOf(0).equals(JsonPath.read(doc, "$.arrayKey[0]"))); + assertTrue("expected 1", + Integer.valueOf(1).equals(JsonPath.read(doc, "$.arrayKey[1]"))); + assertTrue("expected 2", + Integer.valueOf(2).equals(JsonPath.read(doc, "$.arrayKey[2]"))); + assertTrue("expected 4 objectKey items", + ((Map) JsonPath.read(doc, "$.objectKey")).size() == 4); + assertTrue("expected myVal1", + "myVal1".equals(JsonPath.read(doc, "$.objectKey.myKey1"))); + assertTrue("expected myVal2", + "myVal2".equals(JsonPath.read(doc, "$.objectKey.myKey2"))); + assertTrue("expected myVal3", + "myVal3".equals(JsonPath.read(doc, "$.objectKey.myKey3"))); + assertTrue("expected myVal4", + "myVal4".equals(JsonPath.read(doc, "$.objectKey.myKey4"))); + + jsonObject.remove("trueKey"); + final 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())); + + final String aCompareValueStr = "{\"a\":\"aval\",\"b\":true}"; + final String bCompareValueStr = "{\"a\":\"notAval\",\"b\":true}"; + final JSONObject aCompareValueJsonObject = new JSONObject( + aCompareValueStr); + final JSONObject bCompareValueJsonObject = new JSONObject( + bCompareValueStr); + assertTrue("different values should not be similar", + !aCompareValueJsonObject.similar(bCompareValueJsonObject)); + + final String aCompareObjectStr = "{\"a\":\"aval\",\"b\":{}}"; + final String bCompareObjectStr = "{\"a\":\"aval\",\"b\":true}"; + final JSONObject aCompareObjectJsonObject = new JSONObject( + aCompareObjectStr); + final JSONObject bCompareObjectJsonObject = new JSONObject( + bCompareObjectStr); + assertTrue("different nested JSONObjects should not be similar", + !aCompareObjectJsonObject.similar(bCompareObjectJsonObject)); + + final String aCompareArrayStr = "{\"a\":\"aval\",\"b\":[]}"; + final String bCompareArrayStr = "{\"a\":\"aval\",\"b\":true}"; + final JSONObject aCompareArrayJsonObject = new JSONObject( + aCompareArrayStr); + final JSONObject bCompareArrayJsonObject = new JSONObject( + bCompareArrayStr); + assertTrue("different nested JSONArrays should not be similar", + !aCompareArrayJsonObject.similar(bCompareArrayJsonObject)); + } + /** * Confirm behavior when JSONObject put(key, null object) is called */ @@ -1711,11 +1430,11 @@ public class JSONObjectTest { public void jsonObjectputNull() { // put null should remove the item. - String str = "{\"myKey\": \"myval\"}"; - JSONObject jsonObjectRemove = new JSONObject(str); + final String str = "{\"myKey\": \"myval\"}"; + final JSONObject jsonObjectRemove = new JSONObject(str); jsonObjectRemove.remove("myKey"); - JSONObject jsonObjectPutNull = new JSONObject(str); + final JSONObject jsonObjectPutNull = new JSONObject(str); jsonObjectPutNull.put("myKey", (Object) null); // validate JSON @@ -1724,9 +1443,18 @@ public class JSONObjectTest { } /** - * Exercise JSONObject quote() method - * This purpose of quote() is to ensure that for strings with embedded - * quotes, the quotes are properly escaped. + * Confirm behavior when putOnce() is called with null parameters + */ + @Test + public void jsonObjectPutOnceNull() { + final JSONObject jsonObject = new JSONObject(); + jsonObject.putOnce(null, null); + assertTrue("jsonObject should be empty", jsonObject.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() { @@ -1734,50 +1462,281 @@ public class JSONObjectTest { str = ""; String quotedStr; quotedStr = JSONObject.quote(str); - assertTrue("quote() expected escaped quotes, found "+quotedStr, + assertTrue("quote() expected escaped quotes, found " + quotedStr, "\"\"".equals(quotedStr)); str = "\"\""; quotedStr = JSONObject.quote(str); - assertTrue("quote() expected escaped quotes, found "+quotedStr, + assertTrue("quote() expected escaped quotes, found " + quotedStr, "\"\\\"\\\"\"".equals(quotedStr)); str = ") JsonPath.read(doc, "$")).size() == 4); + assertTrue("expected true", + Boolean.TRUE.equals(JsonPath.read(doc, "$.trueKey"))); + assertTrue("expected false", + Boolean.FALSE.equals(JsonPath.read(doc, "$.falseKey"))); + assertTrue("expected 3 arrayKey items", + ((List) JsonPath.read(doc, "$.arrayKey")).size() == 3); + assertTrue("expected 0", + Integer.valueOf(0).equals(JsonPath.read(doc, "$.arrayKey[0]"))); + assertTrue("expected 1", + Integer.valueOf(1).equals(JsonPath.read(doc, "$.arrayKey[1]"))); + assertTrue("expected 2", + Integer.valueOf(2).equals(JsonPath.read(doc, "$.arrayKey[2]"))); + assertTrue("expected 4 objectKey items", + ((Map) JsonPath.read(doc, "$.objectKey")).size() == 4); + assertTrue("expected myVal1", + "myVal1".equals(JsonPath.read(doc, "$.objectKey.myKey1"))); + assertTrue("expected myVal2", + "myVal2".equals(JsonPath.read(doc, "$.objectKey.myKey2"))); + assertTrue("expected myVal3", + "myVal3".equals(JsonPath.read(doc, "$.objectKey.myKey3"))); + assertTrue("expected myVal4", + "myVal4".equals(JsonPath.read(doc, "$.objectKey.myKey4"))); + } + + /** + * 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() { + final JSONObject jsonObject = new JSONObject(); + final Collection collection = new ArrayList(); + collection.add("abc"); + // ArrayList will be added as an object + jsonObject.put("key", collection); + + // validate JSON + final 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(JsonPath.read(doc, "$.key[0]"))); + } + + /** + * 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() { + final JSONObject jsonObject = new JSONObject(); + final Map map = new HashMap<>(); + map.put("abc", "def"); + jsonObject.put("key", map); + + // validate JSON + final 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(JsonPath.read(doc, "$.key.abc"))); + } + + /** + * Exercise some JSONObject get[type] and opt[type] methods + */ + @Test + public void jsonObjectValues() { + final 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\"}" + "}"; + final 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); + final JSONObject jsonObjectInner = jsonObject + .getJSONObject("objectKey"); + assertTrue("objectKey should be JSONObject", + jsonObjectInner.get("myKey").equals("myVal")); + } + + /** + * 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 + final String str = "{" + "\"numberWithDecimals\":299792.457999999984," + + "\"largeNumber\":12345678901234567890," + + "\"preciseNumber\":0.2000000000000000111," + + "\"largeExponent\":-23.45e2327" + "}"; + final 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)); + } + + /** + * Confirm behavior when JSONObject stringToValue() is called for an empty + * string */ @Test public void stringToValue() { - String str = ""; - String valueStr = (String)(JSONObject.stringToValue(str)); - assertTrue("stringToValue() expected empty String, found "+valueStr, + final String str = ""; + final String valueStr = (String) JSONObject.stringToValue(str); + assertTrue("stringToValue() expected empty String, found " + valueStr, "".equals(valueStr)); } + /** + * 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 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. + */ + final 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); + + final 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")); + } + /** * Confirm behavior when toJSONArray is called with a null value */ @@ -1787,114 +1746,330 @@ public class JSONObjectTest { null == new JSONObject().toJSONArray(null)); } + /** + * 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() { + final String key30 = "key30"; + final String key31 = "key31"; + final 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. + final String serializedString = jsonObject.toString(); + final 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); + } + + /** + * Exercises the JSONObject.valueToString() method for various types + */ + @Test + public void valueToString() { + + assertTrue("null valueToString() incorrect", + "null".equals(JSONObject.valueToString(null))); + final 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))); + final String jsonObjectStr = "{" + "\"key1\":\"val1\"," + + "\"key2\":\"val2\"," + "\"key3\":\"val3\"" + "}"; + final JSONObject jsonObject = new JSONObject(jsonObjectStr); + assertTrue("jsonObject valueToString() incorrect", JSONObject + .valueToString(jsonObject).equals(jsonObject.toString())); + final String jsonArrayStr = "[1,2,3]"; + final JSONArray jsonArray = new JSONArray(jsonArrayStr); + assertTrue("jsonArra valueToString() incorrect", JSONObject + .valueToString(jsonArray).equals(jsonArray.toString())); + final 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))); + final 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))); + final 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() { + final Map myMap = new HashMap(); + myMap.put(1, "myValue"); + // this is the test, it should not throw an exception + final String str = JSONObject.valueToString(myMap); + // confirm result, just in case + final 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"))); + } + + /** + * 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") + final Map myRawC = Collections.singletonMap("myKey", + Integer.valueOf(10)); + final JSONObject jaRaw = new JSONObject(myRawC); + + final Map myCStrObj = Collections.singletonMap("myKey", + (Object) Integer.valueOf(10)); + final JSONObject jaStrObj = new JSONObject(myCStrObj); + + final Map myCStrInt = Collections.singletonMap( + "myKey", Integer.valueOf(10)); + final JSONObject jaStrInt = new JSONObject(myCStrInt); + + final Map myCObjObj = Collections.singletonMap((Object) "myKey", + (Object) Integer.valueOf(10)); + final 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)); + } + + /** + * 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") + final Collection myRawC = Collections.singleton(Integer.valueOf(10)); + final JSONObject jaRaw = new JSONObject(); + jaRaw.put("myCollection", myRawC); + + final Collection myCObj = Collections + .singleton((Object) Integer.valueOf(10)); + final JSONObject jaObj = new JSONObject(); + jaObj.put("myCollection", myCObj); + + final Collection myCInt = Collections.singleton(Integer + .valueOf(10)); + final 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") + final Map myRawC = Collections.singletonMap("myKey", + Integer.valueOf(10)); + final JSONObject jaRaw = new JSONObject(); + jaRaw.put("myMap", myRawC); + + final Map myCStrObj = Collections.singletonMap("myKey", + (Object) Integer.valueOf(10)); + final JSONObject jaStrObj = new JSONObject(); + jaStrObj.put("myMap", myCStrObj); + + final Map myCStrInt = Collections.singletonMap( + "myKey", Integer.valueOf(10)); + final JSONObject jaStrInt = new JSONObject(); + jaStrInt.put("myMap", myCStrInt); + + final Map myCObjObj = Collections.singletonMap((Object) "myKey", + (Object) Integer.valueOf(10)); + final 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)); + } + + /** + * 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 + final 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 + */ + final Object bdWrap = JSONObject.wrap(BigDecimal.ONE); + assertTrue("BigDecimal.ONE evaluates to ONE", + bdWrap.equals(BigDecimal.ONE)); + + // wrap JSONObject returns JSONObject + final String jsonObjectStr = "{" + "\"key1\":\"val1\"," + + "\"key2\":\"val2\"," + "\"key3\":\"val3\"" + "}"; + final JSONObject jsonObject = new JSONObject(jsonObjectStr); + assertTrue("JSONObject wrap() incorrect", + jsonObject == JSONObject.wrap(jsonObject)); + + // wrap collection returns JSONArray + final Collection collection = new ArrayList(); + collection.add(new Integer(1)); + collection.add(new Integer(2)); + collection.add(new Integer(3)); + final 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(JsonPath.read(doc, "$[0]"))); + assertTrue("expected 2", + Integer.valueOf(2).equals(JsonPath.read(doc, "$[1]"))); + assertTrue("expected 3", + Integer.valueOf(3).equals(JsonPath.read(doc, "$[2]"))); + + // wrap Array returns JSONArray + final Integer[] array = { new Integer(1), new Integer(2), + new Integer(3) }; + final 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(JsonPath.read(doc, "$[0]"))); + assertTrue("expected 2", + Integer.valueOf(2).equals(JsonPath.read(doc, "$[1]"))); + assertTrue("expected 3", + Integer.valueOf(3).equals(JsonPath.read(doc, "$[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(JsonPath.read(doc, "$[0]"))); + assertTrue("expected 2", + Integer.valueOf(2).equals(JsonPath.read(doc, "$[1]"))); + assertTrue("expected 3", + Integer.valueOf(3).equals(JsonPath.read(doc, "$[2]"))); + + // wrap map returns JSONObject + final Map map = new HashMap(); + map.put("key1", "val1"); + map.put("key2", "val2"); + map.put("key3", "val3"); + final 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(JsonPath.read(doc, "$.key1"))); + assertTrue("expected val2", "val2".equals(JsonPath.read(doc, "$.key2"))); + assertTrue("expected val3", "val3".equals(JsonPath.read(doc, "$.key3"))); + } + /** * Exercise the JSONObject write() method */ @Test public void write() { - String str = "{\"key\":\"value\"}"; - String expectedStr = str; - JSONObject jsonObject = new JSONObject(str); - StringWriter stringWriter = new StringWriter(); - Writer writer = jsonObject.write(stringWriter); - String actualStr = writer.toString(); - assertTrue("write() expected " +expectedStr+ - "but found " +actualStr, + final String str = "{\"key\":\"value\"}"; + final String expectedStr = str; + final JSONObject jsonObject = new JSONObject(str); + final StringWriter stringWriter = new StringWriter(); + final Writer writer = jsonObject.write(stringWriter); + final String actualStr = writer.toString(); + assertTrue( + "write() expected " + expectedStr + "but found " + actualStr, expectedStr.equals(actualStr)); } - - /** - * Exercise the JSONObject equals() method - */ - @Test - public void equals() { - String str = "{\"key\":\"value\"}"; - JSONObject aJsonObject = new JSONObject(str); - assertTrue("Same JSONObject should be equal to itself", - aJsonObject.equals(aJsonObject)); - } - - /** - * JSON null is not the same as Java null. This test examines the differences - * in how they are handled by JSON-java. - */ - @Test - public void jsonObjectNullOperations() { - /** - * The Javadoc for JSONObject.NULL states: - * "JSONObject.NULL is equivalent to the value that JavaScript calls null, - * whilst Java's null is equivalent to the value that JavaScript calls - * undefined." - * - * Standard ECMA-262 6th Edition / June 2015 (included to help explain the javadoc): - * undefined value: primitive value used when a variable has not been assigned a value - * Undefined type: type whose sole value is the undefined value - * null value: primitive value that represents the intentional absence of any object value - * Null type: type whose sole value is the null value - * Java SE8 language spec (included to help explain the javadoc): - * The Kinds of Types and Values ... - * There is also a special null type, the type of the expression null, which has no name. - * Because the null type has no name, it is impossible to declare a variable of the null - * type or to cast to the null type. The null reference is the only possible value of an - * expression of null type. The null reference can always be assigned or cast to any reference type. - * In practice, the programmer can ignore the null type and just pretend that null is merely - * a special literal that can be of any reference type. - * Extensible Markup Language (XML) 1.0 Fifth Edition / 26 November 2008 - * No mention of null - * ECMA-404 1st Edition / October 2013: - * JSON Text ... - * These are three literal name tokens: ... - * null - * - * There seems to be no best practice to follow, it's all about what we - * want the code to do. - */ - - // add JSONObject.NULL then convert to string in the manner of XML.toString() - JSONObject jsonObjectJONull = new JSONObject(); - Object obj = JSONObject.NULL; - jsonObjectJONull.put("key", obj); - Object value = jsonObjectJONull.opt("key"); - assertTrue("opt() JSONObject.NULL should find JSONObject.NULL", - obj.equals(value)); - value = jsonObjectJONull.get("key"); - assertTrue("get() JSONObject.NULL should find JSONObject.NULL", - obj.equals(value)); - if (value == null) { - value = ""; - } - String string = value instanceof String ? (String)value : null; - assertTrue("XML toString() should convert JSONObject.NULL to null", - string == null); - - // now try it with null - JSONObject jsonObjectNull = new JSONObject(); - obj = null; - jsonObjectNull.put("key", obj); - value = jsonObjectNull.opt("key"); - assertTrue("opt() null should find null", value == null); - if (value == null) { - value = ""; - } - string = value instanceof String ? (String)value : null; - assertTrue("should convert null to empty string", "".equals(string)); - try { - value = jsonObjectNull.get("key"); - assertTrue("get() null should throw exception", false); - } catch (Exception ignored) {} - - /** - * XML.toString() then goes on to do something with the value - * if the key val is "content", then value.toString() will be - * called. This will evaluate to "null" for JSONObject.NULL, - * and the empty string for null. - * But if the key is anything else, then JSONObject.NULL will be emitted - * as 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)); - } }