@@ -79,17 +79,6 @@ public class JSONObject {
7979 */
8080 private static final class Null {
8181
82- /**
83- * There is only intended to be a single instance of the NULL object,
84- * so the clone method returns itself.
85- *
86- * @return NULL.
87- */
88- @ Override
89- protected final Object clone () {
90- return this ;
91- }
92-
9382 /**
9483 * A Null object is equal to the null value and to itself.
9584 *
@@ -180,7 +169,7 @@ public JSONObject(JSONObject jo, String ... names) {
180169 for (int i = 0 ; i < names .length ; i += 1 ) {
181170 try {
182171 this .putOnce (names [i ], jo .opt (names [i ]));
183- } catch (Exception ignore ) {
172+ } catch (Exception ignore ) { // exception thrown for missing key
184173 }
185174 }
186175 }
@@ -221,83 +210,128 @@ public JSONObject(JSONTokener x, JSONParserConfiguration jsonParserConfiguration
221210 throw x .syntaxError ("A JSONObject text must begin with '{'" );
222211 }
223212 for (;;) {
224- c = x .nextClean ();
225- switch (c ) {
213+ if (parseJSONObject (x , jsonParserConfiguration , isInitial )) {
214+ return ;
215+ }
216+ }
217+ }
218+
219+ /**
220+ * Parses entirety of JSON object
221+ *
222+ * @param jsonTokener Parses text as tokens
223+ * @param jsonParserConfiguration Variable to pass parser custom configuration for json parsing.
224+ * @param isInitial True if start of document, else false
225+ * @return True if done building object, else false
226+ */
227+ private boolean parseJSONObject (JSONTokener jsonTokener , JSONParserConfiguration jsonParserConfiguration , boolean isInitial ) {
228+ Object obj ;
229+ String key ;
230+ char c ;
231+ c = jsonTokener .nextClean ();
232+
233+ switch (c ) {
226234 case 0 :
227- throw x .syntaxError ("A JSONObject text must end with '}'" );
235+ throw jsonTokener .syntaxError ("A JSONObject text must end with '}'" );
228236 case '}' :
229- if (isInitial && jsonParserConfiguration .isStrictMode () && x .nextClean () != 0 ) {
230- throw x .syntaxError ("Strict mode error: Unparsed characters found at end of input text" );
237+ if (isInitial && jsonParserConfiguration .isStrictMode () && jsonTokener .nextClean () != 0 ) {
238+ throw jsonTokener .syntaxError ("Strict mode error: Unparsed characters found at end of input text" );
231239 }
232- return ;
240+ return true ;
233241 default :
234- obj = x .nextSimpleValue (c );
242+ obj = jsonTokener .nextSimpleValue (c );
235243 key = obj .toString ();
236- }
244+ }
237245
238- if (jsonParserConfiguration != null && jsonParserConfiguration .isStrictMode ()) {
239- if (obj instanceof Boolean ) {
240- throw x .syntaxError (String .format ("Strict mode error: key '%s' cannot be boolean" , key ));
241- }
242- if (obj == JSONObject .NULL ) {
243- throw x .syntaxError (String .format ("Strict mode error: key '%s' cannot be null" , key ));
244- }
245- if (obj instanceof Number ) {
246- throw x .syntaxError (String .format ("Strict mode error: key '%s' cannot be number" , key ));
247- }
248- }
246+ checkKeyForStrictMode (jsonTokener , jsonParserConfiguration , obj );
249247
250- // The key is followed by ':'.
248+ // The key is followed by ':'.
249+ c = jsonTokener .nextClean ();
250+ if (c != ':' ) {
251+ throw jsonTokener .syntaxError ("Expected a ':' after a key" );
252+ }
251253
252- c = x .nextClean ();
253- if (c != ':' ) {
254- throw x .syntaxError ("Expected a ':' after a key" );
254+ // Use syntaxError(..) to include error location
255+ if (key != null ) {
256+ // Check if key exists
257+ boolean keyExists = this .opt (key ) != null ;
258+ if (keyExists && !jsonParserConfiguration .isOverwriteDuplicateKey ()) {
259+ throw jsonTokener .syntaxError ("Duplicate key \" " + key + "\" " );
255260 }
256261
257- // Use syntaxError(..) to include error location
258-
259- if (key != null ) {
260- // Check if key exists
261- boolean keyExists = this .opt (key ) != null ;
262- if (keyExists && !jsonParserConfiguration .isOverwriteDuplicateKey ()) {
263- throw x .syntaxError ("Duplicate key \" " + key + "\" " );
264- }
265-
266- Object value = x .nextValue ();
267- // Only add value if non-null
268- if (value != null ) {
269- this .put (key , value );
270- }
262+ Object value = jsonTokener .nextValue ();
263+ // Only add value if non-null
264+ if (value != null ) {
265+ this .put (key , value );
271266 }
267+ }
272268
273- // Pairs are separated by ','.
269+ // Pairs are separated by ','.
270+ if (parseEndOfKeyValuePair (jsonTokener , jsonParserConfiguration , isInitial )) {
271+ return true ;
272+ }
273+ // Not finished parsing
274+ return false ;
275+ }
274276
275- switch (x .nextClean ()) {
277+ /**
278+ * Checks for valid end of key:value pair
279+ * @param jsonTokener Parses text as tokens
280+ * @param jsonParserConfiguration Variable to pass parser custom configuration for json parsing.
281+ * @param isInitial True if end of JSON object, else false
282+ * @return
283+ */
284+ private static boolean parseEndOfKeyValuePair (JSONTokener jsonTokener , JSONParserConfiguration jsonParserConfiguration , boolean isInitial ) {
285+ switch (jsonTokener .nextClean ()) {
276286 case ';' :
277287 // In strict mode semicolon is not a valid separator
278288 if (jsonParserConfiguration .isStrictMode ()) {
279- throw x .syntaxError ("Strict mode error: Invalid character ';' found" );
289+ throw jsonTokener .syntaxError ("Strict mode error: Invalid character ';' found" );
280290 }
291+ break ;
281292 case ',' :
282- if (x .nextClean () == '}' ) {
293+ if (jsonTokener .nextClean () == '}' ) {
283294 // trailing commas are not allowed in strict mode
284295 if (jsonParserConfiguration .isStrictMode ()) {
285- throw x .syntaxError ("Strict mode error: Expected another object element" );
296+ throw jsonTokener .syntaxError ("Strict mode error: Expected another object element" );
286297 }
287- return ;
298+ // End of JSON object
299+ return true ;
288300 }
289- if (x .end ()) {
290- throw x .syntaxError ("A JSONObject text must end with '}'" );
301+ if (jsonTokener .end ()) {
302+ throw jsonTokener .syntaxError ("A JSONObject text must end with '}'" );
291303 }
292- x .back ();
304+ jsonTokener .back ();
293305 break ;
294306 case '}' :
295- if (isInitial && jsonParserConfiguration .isStrictMode () && x .nextClean () != 0 ) {
296- throw x .syntaxError ("Strict mode error: Unparsed characters found at end of input text" );
307+ if (isInitial && jsonParserConfiguration .isStrictMode () && jsonTokener .nextClean () != 0 ) {
308+ throw jsonTokener .syntaxError ("Strict mode error: Unparsed characters found at end of input text" );
297309 }
298- return ;
310+ // End of JSON object
311+ return true ;
299312 default :
300- throw x .syntaxError ("Expected a ',' or '}'" );
313+ throw jsonTokener .syntaxError ("Expected a ',' or '}'" );
314+ }
315+ // Not at end of JSON object
316+ return false ;
317+ }
318+
319+ /**
320+ * Throws error if key violates strictMode
321+ * @param jsonTokener Parses text as tokens
322+ * @param jsonParserConfiguration Variable to pass parser custom configuration for json parsing.
323+ * @param obj Value to be checked
324+ */
325+ private static void checkKeyForStrictMode (JSONTokener jsonTokener , JSONParserConfiguration jsonParserConfiguration , Object obj ) {
326+ if (jsonParserConfiguration != null && jsonParserConfiguration .isStrictMode ()) {
327+ if (obj instanceof Boolean ) {
328+ throw jsonTokener .syntaxError (String .format ("Strict mode error: key '%s' cannot be boolean" , obj .toString ()));
329+ }
330+ if (obj == JSONObject .NULL ) {
331+ throw jsonTokener .syntaxError (String .format ("Strict mode error: key '%s' cannot be null" , obj .toString ()));
332+ }
333+ if (obj instanceof Number ) {
334+ throw jsonTokener .syntaxError (String .format ("Strict mode error: key '%s' cannot be number" , obj .toString ()));
301335 }
302336 }
303337 }
0 commit comments