Skip to content

Commit 78137d3

Browse files
authored
Merge pull request #1001 from marilynel/master
addressing sonarqube concerns in JSONObject
2 parents 82432f0 + 38c3a0b commit 78137d3

File tree

1 file changed

+96
-66
lines changed

1 file changed

+96
-66
lines changed

src/main/java/org/json/JSONObject.java

Lines changed: 96 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -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
}
@@ -211,93 +200,134 @@ public JSONObject(JSONTokener x) throws JSONException {
211200
*/
212201
public JSONObject(JSONTokener x, JSONParserConfiguration jsonParserConfiguration) throws JSONException {
213202
this();
214-
char c;
215-
String key;
216-
Object obj;
217-
218203
boolean isInitial = x.getPrevious() == 0;
219204

220205
if (x.nextClean() != '{') {
221206
throw x.syntaxError("A JSONObject text must begin with '{'");
222207
}
223208
for (;;) {
224-
c = x.nextClean();
225-
switch (c) {
209+
if (parseJSONObject(x, jsonParserConfiguration, isInitial)) {
210+
return;
211+
}
212+
}
213+
}
214+
215+
/**
216+
* Parses entirety of JSON object
217+
*
218+
* @param jsonTokener Parses text as tokens
219+
* @param jsonParserConfiguration Variable to pass parser custom configuration for json parsing.
220+
* @param isInitial True if start of document, else false
221+
* @return True if done building object, else false
222+
*/
223+
private boolean parseJSONObject(JSONTokener jsonTokener, JSONParserConfiguration jsonParserConfiguration, boolean isInitial) {
224+
Object obj;
225+
String key;
226+
boolean doneParsing = false;
227+
char c = jsonTokener.nextClean();
228+
229+
switch (c) {
226230
case 0:
227-
throw x.syntaxError("A JSONObject text must end with '}'");
231+
throw jsonTokener.syntaxError("A JSONObject text must end with '}'");
228232
case '}':
229-
if (isInitial && jsonParserConfiguration.isStrictMode() && x.nextClean() != 0) {
230-
throw x.syntaxError("Strict mode error: Unparsed characters found at end of input text");
233+
if (isInitial && jsonParserConfiguration.isStrictMode() && jsonTokener.nextClean() != 0) {
234+
throw jsonTokener.syntaxError("Strict mode error: Unparsed characters found at end of input text");
231235
}
232-
return;
236+
return true;
233237
default:
234-
obj = x.nextSimpleValue(c);
238+
obj = jsonTokener.nextSimpleValue(c);
235239
key = obj.toString();
236-
}
240+
}
237241

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-
}
242+
checkKeyForStrictMode(jsonTokener, jsonParserConfiguration, obj);
249243

250-
// The key is followed by ':'.
244+
// The key is followed by ':'.
245+
c = jsonTokener.nextClean();
246+
if (c != ':') {
247+
throw jsonTokener.syntaxError("Expected a ':' after a key");
248+
}
251249

252-
c = x.nextClean();
253-
if (c != ':') {
254-
throw x.syntaxError("Expected a ':' after a key");
250+
// Use syntaxError(..) to include error location
251+
if (key != null) {
252+
// Check if key exists
253+
boolean keyExists = this.opt(key) != null;
254+
if (keyExists && !jsonParserConfiguration.isOverwriteDuplicateKey()) {
255+
throw jsonTokener.syntaxError("Duplicate key \"" + key + "\"");
255256
}
256257

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-
}
258+
Object value = jsonTokener.nextValue();
259+
// Only add value if non-null
260+
if (value != null) {
261+
this.put(key, value);
271262
}
263+
}
272264

273-
// Pairs are separated by ','.
265+
// Pairs are separated by ','.
266+
if (parseEndOfKeyValuePair(jsonTokener, jsonParserConfiguration, isInitial)) {
267+
doneParsing = true;
268+
}
274269

275-
switch (x.nextClean()) {
270+
return doneParsing;
271+
}
272+
273+
/**
274+
* Checks for valid end of key:value pair
275+
* @param jsonTokener Parses text as tokens
276+
* @param jsonParserConfiguration Variable to pass parser custom configuration for json parsing.
277+
* @param isInitial True if end of JSON object, else false
278+
* @return
279+
*/
280+
private static boolean parseEndOfKeyValuePair(JSONTokener jsonTokener, JSONParserConfiguration jsonParserConfiguration, boolean isInitial) {
281+
switch (jsonTokener.nextClean()) {
276282
case ';':
277283
// In strict mode semicolon is not a valid separator
278284
if (jsonParserConfiguration.isStrictMode()) {
279-
throw x.syntaxError("Strict mode error: Invalid character ';' found");
285+
throw jsonTokener.syntaxError("Strict mode error: Invalid character ';' found");
280286
}
287+
break;
281288
case ',':
282-
if (x.nextClean() == '}') {
289+
if (jsonTokener.nextClean() == '}') {
283290
// trailing commas are not allowed in strict mode
284291
if (jsonParserConfiguration.isStrictMode()) {
285-
throw x.syntaxError("Strict mode error: Expected another object element");
292+
throw jsonTokener.syntaxError("Strict mode error: Expected another object element");
286293
}
287-
return;
294+
// End of JSON object
295+
return true;
288296
}
289-
if (x.end()) {
290-
throw x.syntaxError("A JSONObject text must end with '}'");
297+
if (jsonTokener.end()) {
298+
throw jsonTokener.syntaxError("A JSONObject text must end with '}'");
291299
}
292-
x.back();
300+
jsonTokener.back();
293301
break;
294302
case '}':
295-
if (isInitial && jsonParserConfiguration.isStrictMode() && x.nextClean() != 0) {
296-
throw x.syntaxError("Strict mode error: Unparsed characters found at end of input text");
303+
if (isInitial && jsonParserConfiguration.isStrictMode() && jsonTokener.nextClean() != 0) {
304+
throw jsonTokener.syntaxError("Strict mode error: Unparsed characters found at end of input text");
297305
}
298-
return;
306+
// End of JSON object
307+
return true;
299308
default:
300-
throw x.syntaxError("Expected a ',' or '}'");
309+
throw jsonTokener.syntaxError("Expected a ',' or '}'");
310+
}
311+
// Not at end of JSON object
312+
return false;
313+
}
314+
315+
/**
316+
* Throws error if key violates strictMode
317+
* @param jsonTokener Parses text as tokens
318+
* @param jsonParserConfiguration Variable to pass parser custom configuration for json parsing.
319+
* @param obj Value to be checked
320+
*/
321+
private static void checkKeyForStrictMode(JSONTokener jsonTokener, JSONParserConfiguration jsonParserConfiguration, Object obj) {
322+
if (jsonParserConfiguration != null && jsonParserConfiguration.isStrictMode()) {
323+
if(obj instanceof Boolean) {
324+
throw jsonTokener.syntaxError(String.format("Strict mode error: key '%s' cannot be boolean", obj.toString()));
325+
}
326+
if(obj == JSONObject.NULL) {
327+
throw jsonTokener.syntaxError(String.format("Strict mode error: key '%s' cannot be null", obj.toString()));
328+
}
329+
if(obj instanceof Number) {
330+
throw jsonTokener.syntaxError(String.format("Strict mode error: key '%s' cannot be number", obj.toString()));
301331
}
302332
}
303333
}

0 commit comments

Comments
 (0)