1
1
package org .json ;
2
2
3
+ import java .io .Closeable ;
4
+
3
5
/*
4
6
Copyright (c) 2002 JSON.org
5
7
@@ -28,6 +30,7 @@ of this software and associated documentation files (the "Software"), to deal
28
30
import java .io .StringWriter ;
29
31
import java .io .Writer ;
30
32
import java .lang .reflect .Field ;
33
+ import java .lang .reflect .InvocationTargetException ;
31
34
import java .lang .reflect .Method ;
32
35
import java .lang .reflect .Modifier ;
33
36
import java .math .BigDecimal ;
@@ -228,7 +231,21 @@ public JSONObject(JSONTokener x) throws JSONException {
228
231
if (c != ':' ) {
229
232
throw x .syntaxError ("Expected a ':' after a key" );
230
233
}
231
- this .putOnce (key , x .nextValue ());
234
+
235
+ // Use syntaxError(..) to include error location
236
+
237
+ if (key != null ) {
238
+ // Check if key exists
239
+ if (this .opt (key ) != null ) {
240
+ // key already exists
241
+ throw x .syntaxError ("Duplicate key \" " + key + "\" " );
242
+ }
243
+ // Only add value if non-null
244
+ Object value = x .nextValue ();
245
+ if (value !=null ) {
246
+ this .put (key , value );
247
+ }
248
+ }
232
249
233
250
// Pairs are separated by ','.
234
251
@@ -276,16 +293,19 @@ public JSONObject(Map<?, ?> m) {
276
293
* <code>"is"</code> followed by an uppercase letter, the method is invoked,
277
294
* and a key and the value returned from the getter method are put into the
278
295
* new JSONObject.
279
- *
296
+ * <p>
280
297
* The key is formed by removing the <code>"get"</code> or <code>"is"</code>
281
298
* prefix. If the second remaining character is not upper case, then the
282
299
* first character is converted to lower case.
283
- *
300
+ * <p>
284
301
* For example, if an object has a method named <code>"getName"</code>, and
285
302
* if the result of calling <code>object.getName()</code> is
286
303
* <code>"Larry Fine"</code>, then the JSONObject will contain
287
304
* <code>"name": "Larry Fine"</code>.
288
- *
305
+ * <p>
306
+ * Methods that return <code>void</code> as well as <code>static</code>
307
+ * methods are ignored.
308
+ *
289
309
* @param bean
290
310
* An object that has getter methods that should be used to make
291
311
* a JSONObject.
@@ -1388,6 +1408,15 @@ public String optString(String key, String defaultValue) {
1388
1408
return NULL .equals (object ) ? defaultValue : object .toString ();
1389
1409
}
1390
1410
1411
+ /**
1412
+ * Populates the internal map of the JSONObject with the bean properties.
1413
+ * The bean can not be recursive.
1414
+ *
1415
+ * @see JSONObject#JSONObject(Object)
1416
+ *
1417
+ * @param bean
1418
+ * the bean
1419
+ */
1391
1420
private void populateMap (Object bean ) {
1392
1421
Class <?> klass = bean .getClass ();
1393
1422
@@ -1397,39 +1426,52 @@ private void populateMap(Object bean) {
1397
1426
1398
1427
Method [] methods = includeSuperClass ? klass .getMethods () : klass
1399
1428
.getDeclaredMethods ();
1400
- for (int i = 0 ; i < methods .length ; i += 1 ) {
1401
- try {
1402
- Method method = methods [i ];
1403
- if (Modifier .isPublic (method .getModifiers ())) {
1404
- String name = method .getName ();
1405
- String key = "" ;
1406
- if (name .startsWith ("get" )) {
1407
- if ("getClass" .equals (name )
1408
- || "getDeclaringClass" .equals (name )) {
1409
- key = "" ;
1410
- } else {
1411
- key = name .substring (3 );
1412
- }
1413
- } else if (name .startsWith ("is" )) {
1414
- key = name .substring (2 );
1429
+ for (final Method method : methods ) {
1430
+ final int modifiers = method .getModifiers ();
1431
+ if (Modifier .isPublic (modifiers )
1432
+ && !Modifier .isStatic (modifiers )
1433
+ && method .getParameterTypes ().length == 0
1434
+ && !method .isBridge ()
1435
+ && method .getReturnType () != Void .TYPE ) {
1436
+ final String name = method .getName ();
1437
+ String key ;
1438
+ if (name .startsWith ("get" )) {
1439
+ if ("getClass" .equals (name ) || "getDeclaringClass" .equals (name )) {
1440
+ continue ;
1441
+ }
1442
+ key = name .substring (3 );
1443
+ } else if (name .startsWith ("is" )) {
1444
+ key = name .substring (2 );
1445
+ } else {
1446
+ continue ;
1447
+ }
1448
+ if (key .length () > 0
1449
+ && Character .isUpperCase (key .charAt (0 ))) {
1450
+ if (key .length () == 1 ) {
1451
+ key = key .toLowerCase (Locale .ROOT );
1452
+ } else if (!Character .isUpperCase (key .charAt (1 ))) {
1453
+ key = key .substring (0 , 1 ).toLowerCase (Locale .ROOT )
1454
+ + key .substring (1 );
1415
1455
}
1416
- if (key .length () > 0
1417
- && Character .isUpperCase (key .charAt (0 ))
1418
- && method .getParameterTypes ().length == 0 ) {
1419
- if (key .length () == 1 ) {
1420
- key = key .toLowerCase (Locale .ROOT );
1421
- } else if (!Character .isUpperCase (key .charAt (1 ))) {
1422
- key = key .substring (0 , 1 ).toLowerCase (Locale .ROOT )
1423
- + key .substring (1 );
1424
- }
1425
1456
1426
- Object result = method .invoke (bean , (Object []) null );
1457
+ try {
1458
+ final Object result = method .invoke (bean );
1427
1459
if (result != null ) {
1428
1460
this .map .put (key , wrap (result ));
1461
+ // we don't use the result anywhere outside of wrap
1462
+ // if it's a resource we should be sure to close it after calling toString
1463
+ if (result instanceof Closeable ) {
1464
+ try {
1465
+ ((Closeable )result ).close ();
1466
+ } catch (IOException ignore ) {
1467
+ }
1468
+ }
1429
1469
}
1470
+ } catch (IllegalAccessException ignore ) {
1471
+ } catch (IllegalArgumentException ignore ) {
1472
+ } catch (InvocationTargetException ignore ) {
1430
1473
}
1431
1474
}
1432
- } catch (Exception ignore ) {
1433
1475
}
1434
1476
}
1435
1477
}
@@ -1676,7 +1718,7 @@ public Object optQuery(String jsonPointer) {
1676
1718
* Queries and returns a value from this object using {@code jsonPointer}, or
1677
1719
* returns null if the query fails due to a missing key.
1678
1720
*
1679
- * @param The JSON pointer
1721
+ * @param jsonPointer The JSON pointer
1680
1722
* @return the queried value or {@code null}
1681
1723
* @throws IllegalArgumentException if {@code jsonPointer} has invalid syntax
1682
1724
*/
@@ -2004,9 +2046,10 @@ public JSONArray toJSONArray(JSONArray names) throws JSONException {
2004
2046
* Make a JSON text of this JSONObject. For compactness, no whitespace is
2005
2047
* added. If this would not result in a syntactically correct JSON text,
2006
2048
* then null will be returned instead.
2007
- * <p>
2049
+ * <p><b>
2008
2050
* Warning: This method assumes that the data structure is acyclical.
2009
- *
2051
+ * </b>
2052
+ *
2010
2053
* @return a printable, displayable, portable, transmittable representation
2011
2054
* of the object, beginning with <code>{</code> <small>(left
2012
2055
* brace)</small> and ending with <code>}</code> <small>(right
@@ -2023,8 +2066,20 @@ public String toString() {
2023
2066
2024
2067
/**
2025
2068
* Make a pretty-printed JSON text of this JSONObject.
2026
- * <p>
2069
+ *
2070
+ * <p>If <code>indentFactor > 0</code> and the {@link JSONObject}
2071
+ * has only one key, then the object will be output on a single line:
2072
+ * <pre>{@code {"key": 1}}</pre>
2073
+ *
2074
+ * <p>If an object has 2 or more keys, then it will be output across
2075
+ * multiple lines: <code><pre>{
2076
+ * "key1": 1,
2077
+ * "key2": "value 2",
2078
+ * "key3": 3
2079
+ * }</pre></code>
2080
+ * <p><b>
2027
2081
* Warning: This method assumes that the data structure is acyclical.
2082
+ * </b>
2028
2083
*
2029
2084
* @param indentFactor
2030
2085
* The number of spaces to add to each level of indentation.
@@ -2130,9 +2185,10 @@ public static Object wrap(Object object) {
2130
2185
/**
2131
2186
* Write the contents of the JSONObject as JSON text to a writer. For
2132
2187
* compactness, no whitespace is added.
2133
- * <p>
2188
+ * <p><b>
2134
2189
* Warning: This method assumes that the data structure is acyclical.
2135
- *
2190
+ * </b>
2191
+ *
2136
2192
* @return The writer.
2137
2193
* @throws JSONException
2138
2194
*/
@@ -2196,8 +2252,20 @@ static final void indent(Writer writer, int indent) throws IOException {
2196
2252
2197
2253
/**
2198
2254
* Write the contents of the JSONObject as JSON text to a writer.
2199
- * <p>
2255
+ *
2256
+ * <p>If <code>indentFactor > 0</code> and the {@link JSONObject}
2257
+ * has only one key, then the object will be output on a single line:
2258
+ * <pre>{@code {"key": 1}}</pre>
2259
+ *
2260
+ * <p>If an object has 2 or more keys, then it will be output across
2261
+ * multiple lines: <code><pre>{
2262
+ * "key1": 1,
2263
+ * "key2": "value 2",
2264
+ * "key3": 3
2265
+ * }</pre></code>
2266
+ * <p><b>
2200
2267
* Warning: This method assumes that the data structure is acyclical.
2268
+ * </b>
2201
2269
*
2202
2270
* @param writer
2203
2271
* Writes the serialized JSON
0 commit comments