package org.json.junit; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.io.StringReader; import java.io.StringWriter; import java.io.Writer; import java.math.BigDecimal; import java.math.BigInteger; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import org.json.CDL; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.json.XML; import org.junit.Test; import com.jayway.jsonpath.Configuration; import com.jayway.jsonpath.JsonPath; /** * JSONObject, along with JSONArray, are the central classes of the reference * app. All of the other classes interact with them, and JSON functionality * would otherwise be impossible. */ public class JSONObjectTest { /** * 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. */ 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); assertTrue("JSONObject parses BigInteger as the Integer lowestBitSet", obj instanceof Integer); assertTrue("this bigInteger lowestBitSet happens to be 1", obj.equals(1)); /** * JSONObject tries to parse BigDecimal as a bean, but it has no * getters, The value is lost and no value is stored. This should be * fixed. */ 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 = new JSONObject(); jsonObject.put("bigInt", bigInteger); assertTrue("jsonObject.put() handles bigInt correctly", jsonObject.get("bigInt").equals(bigInteger)); assertTrue("jsonObject.getBigInteger() handles bigInt correctly", jsonObject.getBigInteger("bigInt").equals(bigInteger)); assertTrue( "jsonObject.optBigInteger() handles bigInt correctly", jsonObject.optBigInteger("bigInt", BigInteger.ONE).equals( bigInteger)); assertTrue( "jsonObject serializes bigInt correctly", jsonObject.toString().equals( "{\"bigInt\":123456789012345678901234567890}")); jsonObject = new JSONObject(); jsonObject.put("bigDec", bigDecimal); assertTrue("jsonObject.put() handles bigDec correctly", jsonObject.get("bigDec").equals(bigDecimal)); assertTrue("jsonObject.getBigDecimal() handles bigDec correctly", jsonObject.getBigDecimal("bigDec").equals(bigDecimal)); assertTrue( "jsonObject.optBigDecimal() handles bigDec correctly", jsonObject.optBigDecimal("bigDec", BigDecimal.ONE).equals( bigDecimal)); assertTrue( "jsonObject serializes bigDec correctly", jsonObject .toString() .equals("{\"bigDec\":123456789012345678901234567890.12345678901234567890123456789}")); /** * exercise some exceptions */ try { jsonObject.getBigDecimal("bigInt"); assertTrue("expected an exeption", false); } catch (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 (final JSONException ignored) { } jsonObject.put("stringKey", "abc"); try { jsonObject.getBigDecimal("stringKey"); assertTrue("expected an exeption", false); } catch (final JSONException ignored) { } obj = jsonObject.optBigInteger("bigDec", BigInteger.ONE); assertTrue("expected BigInteger", obj.equals(BigInteger.ONE)); /** * JSONObject.numberToString() works correctly, nothing to change. */ String str = JSONObject.numberToString(bigInteger); assertTrue("numberToString() handles bigInteger correctly", str.equals("123456789012345678901234567890")); str = JSONObject.numberToString(bigDecimal); assertTrue( "numberToString() handles bigDecimal correctly", str.equals("123456789012345678901234567890.12345678901234567890123456789")); /** * JSONObject.stringToValue() turns bigInt into an accurate string, and * rounds bigDec. This incorrect, but users may have come to expect this * behavior. Change would be marginally better, but might inconvenience * users. */ obj = JSONObject.stringToValue(bigInteger.toString()); assertTrue("stringToValue() turns bigInteger string into string", obj instanceof String); obj = JSONObject.stringToValue(bigDecimal.toString()); assertTrue("stringToValue() changes bigDecimal string", !obj.toString() .equals(bigDecimal.toString())); /** * wrap() vs put() big number behavior is now the same. */ // bigInt map ctor Map map = new HashMap(); map.put("bigInt", bigInteger); jsonObject = new JSONObject(map); String actualFromMapStr = jsonObject.toString(); assertTrue("bigInt in map (or array or bean) is a string", actualFromMapStr .equals("{\"bigInt\":123456789012345678901234567890}")); // bigInt put jsonObject = new JSONObject(); jsonObject.put("bigInt", bigInteger); String actualFromPutStr = jsonObject.toString(); assertTrue("bigInt from put is a number", actualFromPutStr .equals("{\"bigInt\":123456789012345678901234567890}")); // bigDec map ctor map = new HashMap(); map.put("bigDec", bigDecimal); jsonObject = new JSONObject(map); actualFromMapStr = jsonObject.toString(); assertTrue( "bigDec in map (or array or bean) is a bigDec", actualFromMapStr .equals("{\"bigDec\":123456789012345678901234567890.12345678901234567890123456789}")); // bigDec put jsonObject = new JSONObject(); jsonObject.put("bigDec", bigDecimal); actualFromPutStr = jsonObject.toString(); assertTrue( "bigDec from put is a number", actualFromPutStr .equals("{\"bigDec\":123456789012345678901234567890.12345678901234567890123456789}")); // bigInt,bigDec put JSONArray jsonArray = new JSONArray(); jsonArray.put(bigInteger); jsonArray.put(bigDecimal); actualFromPutStr = jsonArray.toString(); assertTrue( "bigInt, bigDec from put is a number", actualFromPutStr .equals("[123456789012345678901234567890,123456789012345678901234567890.12345678901234567890123456789]")); assertTrue("getBigInt is bigInt", jsonArray.getBigInteger(0).equals(bigInteger)); assertTrue("getBigDec is bigDec", jsonArray.getBigDecimal(1).equals(bigDecimal)); assertTrue("optBigInt is bigInt", jsonArray.optBigInteger(0, BigInteger.ONE).equals(bigInteger)); assertTrue("optBigDec is bigDec", jsonArray.optBigDecimal(1, BigDecimal.ONE).equals(bigDecimal)); jsonArray.put(Boolean.TRUE); try { jsonArray.getBigInteger(2); assertTrue("should not be able to get big int", false); } catch (final Exception ignored) { } try { jsonArray.getBigDecimal(2); assertTrue("should not be able to get big dec", false); } 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 final List list = new ArrayList(); list.add(bigInteger); list.add(bigDecimal); jsonArray = new JSONArray(list); 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")); jsonObject = new JSONObject(myBigNumberBean); String actualFromBeanStr = jsonObject.toString(); // can't do a full string compare because mockery adds an extra // key/value assertTrue("bigInt from bean ctor is a bigInt", actualFromBeanStr.contains("123456789012345678901234567890")); // bigDec bean ctor myBigNumberBean = mock(MyBigNumberBean.class); when(myBigNumberBean.getBigDecimal()) .thenReturn( new BigDecimal( "123456789012345678901234567890.12345678901234567890123456789")); jsonObject = new JSONObject(myBigNumberBean); actualFromBeanStr = jsonObject.toString(); // can't do a full string compare because mockery adds an extra // key/value assertTrue( "bigDec from bean ctor is a bigDec", actualFromBeanStr .contains("123456789012345678901234567890.12345678901234567890123456789")); // bigInt,bigDec wrap() obj = JSONObject.wrap(bigInteger); assertTrue("wrap() returns big num", obj.equals(bigInteger)); obj = JSONObject.wrap(bigDecimal); assertTrue("wrap() returns string", obj.equals(bigDecimal)); } /** * 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 public void jsonObjectNames() { JSONObject jsonObject; // getNames() from null JSONObject assertTrue("null names from null Object", null == JSONObject.getNames((Object) null)); // getNames() from object with no fields assertTrue("null names from Object with no fields", null == JSONObject.getNames(new MyJsonString())); // getNames from new JSONOjbect jsonObject = new JSONObject(); String[] names = JSONObject.getNames(jsonObject); assertTrue("names should be null", names == null); // getNames() from empty JSONObject final String emptyStr = "{}"; jsonObject = new JSONObject(emptyStr); assertTrue("empty JSONObject should have null names", null == JSONObject.getNames(jsonObject)); // getNames() from JSONObject final String str = "{" + "\"trueKey\":true," + "\"falseKey\":false," + "\"stringKey\":\"hello world!\"," + "}"; jsonObject = new JSONObject(str); names = JSONObject.getNames(jsonObject); JSONArray jsonArray = new JSONArray(names); // validate JSON Object doc = Configuration.defaultConfiguration().jsonProvider() .parse(jsonArray.toString()); List docList = JsonPath.read(doc, "$"); assertTrue("expected 3 items", docList.size() == 3); assertTrue( "expected to find trueKey", ((List) JsonPath.read(doc, "$[?(@=='trueKey')]")).size() == 1); assertTrue( "expected to find falseKey", ((List) JsonPath.read(doc, "$[?(@=='falseKey')]")).size() == 1); assertTrue( "expected to find stringKey", ((List) JsonPath.read(doc, "$[?(@=='stringKey')]")).size() == 1); /** * getNames() from an enum with properties has an interesting result. It * returns the enum values, not the selected enum properties */ final MyEnumField myEnumField = MyEnumField.VAL1; names = JSONObject.getNames(myEnumField); // validate JSON jsonArray = new JSONArray(names); doc = Configuration.defaultConfiguration().jsonProvider() .parse(jsonArray.toString()); docList = JsonPath.read(doc, "$"); assertTrue("expected 3 items", docList.size() == 3); assertTrue("expected to find VAL1", ((List) JsonPath.read(doc, "$[?(@=='VAL1')]")).size() == 1); assertTrue("expected to find VAL2", ((List) JsonPath.read(doc, "$[?(@=='VAL2')]")).size() == 1); assertTrue("expected to find VAL3", ((List) JsonPath.read(doc, "$[?(@=='VAL3')]")).size() == 1); /** * A bean is also an object. But in order to test the static method * getNames(), this particular bean needs some public data members. */ final MyPublicClass myPublicClass = new MyPublicClass(); names = JSONObject.getNames(myPublicClass); // validate JSON jsonArray = new JSONArray(names); doc = Configuration.defaultConfiguration().jsonProvider() .parse(jsonArray.toString()); docList = JsonPath.read(doc, "$"); assertTrue("expected 2 items", docList.size() == 2); assertTrue("expected to find publicString", ((List) JsonPath.read( doc, "$[?(@=='publicString')]")).size() == 1); assertTrue( "expected to find publicInt", ((List) JsonPath.read(doc, "$[?(@=='publicInt')]")).size() == 1); } /** * Populate a JSONArray from a JSONObject names() method. Confirm that it * contains the expected names. */ @Test public void jsonObjectNamesToJsonAray() { final String str = "{" + "\"trueKey\":true," + "\"falseKey\":false," + "\"stringKey\":\"hello world!\"," + "}"; final JSONObject jsonObject = new JSONObject(str); final JSONArray jsonArray = jsonObject.names(); // validate JSON 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); } /** * Tests how JSONObject get[type] handles incorrect types */ @Test 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() { /** * 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) { } /** * 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)); } /** * Exercise JSONObject numberToString() method */ @Test public void jsonObjectNumberToString() { String str; Double dVal; final Integer iVal = 1; str = JSONObject.numberToString(iVal); assertTrue("expected " + iVal + " actual " + str, iVal.toString() .equals(str)); dVal = 12.34; str = JSONObject.numberToString(dVal); assertTrue("expected " + dVal + " actual " + str, dVal.toString() .equals(str)); dVal = 12.34e27; str = JSONObject.numberToString(dVal); assertTrue("expected " + dVal + " actual " + str, dVal.toString() .equals(str)); // trailing .0 is truncated, so it doesn't quite match toString() dVal = 5000000.0000000; str = JSONObject.numberToString(dVal); assertTrue("expected 5000000 actual " + str, str.equals("5000000")); } /** * Exercise JSONObject opt(key, default) method */ @Test public void jsonObjectOptDefault() { final String str = "{\"myKey\": \"myval\"}"; final JSONObject jsonObject = new JSONObject(str); assertTrue("optBoolean() should return default boolean", Boolean.TRUE == jsonObject.optBoolean("myKey", Boolean.TRUE)); assertTrue("optInt() should return default int", 42 == jsonObject.optInt("myKey", 42)); assertTrue("optInt() should return default int", 42 == jsonObject.optInt("myKey", 42)); assertTrue("optLong() should return default long", 42 == jsonObject.optLong("myKey", 42)); assertTrue("optDouble() should return default double", 42.3 == jsonObject.optDouble("myKey", 42.3)); assertTrue("optString() should return default string", "hi".equals(jsonObject.optString("hiKey", "hi"))); } /** * 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 */ @Test public void jsonObjectputNull() { // put null should remove the item. final String str = "{\"myKey\": \"myval\"}"; final JSONObject jsonObjectRemove = new JSONObject(str); jsonObjectRemove.remove("myKey"); final JSONObject jsonObjectPutNull = new JSONObject(str); jsonObjectPutNull.put("myKey", (Object) null); // validate JSON assertTrue("jsonObject should be empty", jsonObjectRemove.length() == 0 && jsonObjectPutNull.length() == 0); } /** * 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() { String str; str = ""; String quotedStr; quotedStr = JSONObject.quote(str); assertTrue("quote() expected escaped quotes, found " + quotedStr, "\"\"".equals(quotedStr)); str = "\"\""; quotedStr = JSONObject.quote(str); assertTrue("quote() expected escaped quotes, found " + quotedStr, "\"\\\"\\\"\"".equals(quotedStr)); str = ") 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() { 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 */ @Test public void toJSONArray() { assertTrue("toJSONArray() with null names should be null", 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() { 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)); } }