Skip to content

Commit 2dcef89

Browse files
Sean LearySean Leary
authored andcommitted
Code review action items - add comments and consistent error messages for strict mode
1 parent d3c7eaf commit 2dcef89

File tree

7 files changed

+77
-49
lines changed

7 files changed

+77
-49
lines changed

src/main/java/org/json/JSONArray.java

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,10 @@ public class JSONArray implements Iterable<Object> {
6767
*/
6868
private final ArrayList<Object> myArrayList;
6969

70+
// strict mode checks after constructor require access to this object
7071
private JSONTokener jsonTokener;
7172

73+
// strict mode checks after constructor require access to this object
7274
private JSONParserConfiguration jsonParserConfiguration;
7375

7476
/**
@@ -138,8 +140,16 @@ public JSONArray(JSONTokener x, JSONParserConfiguration jsonParserConfiguration)
138140
throw x.syntaxError("Expected a ',' or ']'");
139141
}
140142
if (nextChar == ']') {
143+
// trailing commas are not allowed in strict mode
141144
if (jsonParserConfiguration.isStrictMode()) {
142-
throw x.syntaxError("Expected another array element");
145+
throw x.syntaxError("Strict mode error: Expected another array element");
146+
}
147+
return;
148+
}
149+
if (nextChar == ',') {
150+
// consecutive commas are not allowed in strict mode
151+
if (jsonParserConfiguration.isStrictMode()) {
152+
throw x.syntaxError("Strict mode error: Expected a valid array element");
143153
}
144154
return;
145155
}
@@ -166,12 +176,10 @@ public JSONArray(JSONTokener x, JSONParserConfiguration jsonParserConfiguration)
166176
*/
167177
public JSONArray(String source) throws JSONException {
168178
this(source, new JSONParserConfiguration());
169-
if (this.jsonParserConfiguration.isStrictMode()) {
170-
char c = jsonTokener.nextClean();
171-
if (c != 0) {
172-
throw jsonTokener.syntaxError(String.format("invalid character '%s' found after end of array", c));
173-
174-
}
179+
// Strict mode does not allow trailing chars
180+
if (this.jsonParserConfiguration.isStrictMode() &&
181+
this.jsonTokener.nextClean() != 0) {
182+
throw jsonTokener.syntaxError("Strict mode error: Unparsed characters found at end of input text");
175183
}
176184
}
177185

@@ -188,12 +196,10 @@ public JSONArray(String source) throws JSONException {
188196
*/
189197
public JSONArray(String source, JSONParserConfiguration jsonParserConfiguration) throws JSONException {
190198
this(new JSONTokener(source), jsonParserConfiguration);
191-
if (this.jsonParserConfiguration.isStrictMode()) {
192-
char c = jsonTokener.nextClean();
193-
if (c != 0) {
194-
throw jsonTokener.syntaxError(String.format("invalid character '%s' found after end of array", c));
195-
196-
}
199+
// Strict mode does not allow trailing chars
200+
if (this.jsonParserConfiguration.isStrictMode() &&
201+
this.jsonTokener.nextClean() != 0) {
202+
throw jsonTokener.syntaxError("Strict mode error: Unparsed characters found at end of input text");
197203
}
198204
}
199205

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

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -152,8 +152,10 @@ public Class<? extends Map> getMapType() {
152152
*/
153153
public static final Object NULL = new Null();
154154

155+
// strict mode checks after constructor require access to this object
155156
private JSONTokener jsonTokener;
156157

158+
// strict mode checks after constructor require access to this object
157159
private JSONParserConfiguration jsonParserConfiguration;
158160

159161
/**
@@ -268,13 +270,15 @@ public JSONObject(JSONTokener x, JSONParserConfiguration jsonParserConfiguration
268270

269271
switch (x.nextClean()) {
270272
case ';':
273+
// In strict mode semicolon is not a valid separator
271274
if (jsonParserConfiguration.isStrictMode()) {
272-
throw x.syntaxError("Invalid character ';' found in object in strict mode");
275+
throw x.syntaxError("Strict mode error: Invalid character ';' found");
273276
}
274277
case ',':
275278
if (x.nextClean() == '}') {
279+
// trailing commas are not allowed in strict mode
276280
if (jsonParserConfiguration.isStrictMode()) {
277-
throw x.syntaxError("Expected another object element");
281+
throw x.syntaxError("Strict mode error: Expected another object element");
278282
}
279283
return;
280284
}
@@ -452,9 +456,10 @@ public JSONObject(Object object, String ... names) {
452456
*/
453457
public JSONObject(String source) throws JSONException {
454458
this(source, new JSONParserConfiguration());
459+
// Strict mode does not allow trailing chars
455460
if (this.jsonParserConfiguration.isStrictMode() &&
456461
this.jsonTokener.nextClean() != 0) {
457-
throw new JSONException("Unparsed characters found at end of input text");
462+
throw new JSONException("Strict mode error: Unparsed characters found at end of input text");
458463
}
459464
}
460465

@@ -474,12 +479,10 @@ public JSONObject(String source) throws JSONException {
474479
*/
475480
public JSONObject(String source, JSONParserConfiguration jsonParserConfiguration) throws JSONException {
476481
this(new JSONTokener(source), jsonParserConfiguration);
477-
if (this.jsonParserConfiguration.isStrictMode()) {
478-
char c = jsonTokener.nextClean();
479-
if (c != 0) {
480-
throw jsonTokener.syntaxError(String.format("invalid character '%s' found after end of array", c));
481-
482-
}
482+
// Strict mode does not allow trailing chars
483+
if (this.jsonParserConfiguration.isStrictMode() &&
484+
this.jsonTokener.nextClean() != 0) {
485+
throw new JSONException("Strict mode error: Unparsed characters found at end of input text");
483486
}
484487
}
485488

src/main/java/org/json/JSONParserConfiguration.java

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,18 @@ public JSONParserConfiguration withOverwriteDuplicateKey(final boolean overwrite
6464
return clone;
6565
}
6666

67+
/**
68+
* Sets the strict mode configuration for the JSON parser with default true value
69+
* <p>
70+
* When strict mode is enabled, the parser will throw a JSONException if it encounters an invalid character
71+
* immediately following the final ']' character in the input. This is useful for ensuring strict adherence to the
72+
* JSON syntax, as any characters after the final closing bracket of a JSON array are considered invalid.
73+
* @return a new JSONParserConfiguration instance with the updated strict mode setting
74+
*/
75+
public JSONParserConfiguration withStrictMode() {
76+
return withStrictMode(true);
77+
}
78+
6779
/**
6880
* Sets the strict mode configuration for the JSON parser.
6981
* <p>
@@ -92,13 +104,7 @@ public boolean isOverwriteDuplicateKey() {
92104
}
93105

94106
/**
95-
* Retrieves the current strict mode setting of the JSON parser.
96-
* <p>
97-
* Strict mode, when enabled, instructs the parser to throw a JSONException if it encounters an invalid character
98-
* immediately following the final ']' character in the input. This ensures strict adherence to the JSON syntax, as
99-
* any characters after the final closing bracket of a JSON array are considered invalid.
100-
*
101-
* @return the current strict mode setting. True if strict mode is enabled, false otherwise.
107+
* @return the current strict mode setting.
102108
*/
103109
public boolean isStrictMode() {
104110
return this.strictMode;

src/main/java/org/json/JSONTokener.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ public class JSONTokener {
3232
/** the number of characters read in the previous line. */
3333
private long characterPreviousLine;
3434

35+
// access to this object is required for strict mode checking
3536
private JSONParserConfiguration jsonParserConfiguration;
3637

3738
/**
@@ -443,10 +444,11 @@ public Object nextValue() throws JSONException {
443444
Object nextSimpleValue(char c) {
444445
String string;
445446

447+
// Strict mode only allows strings with explicit double quotes
446448
if (jsonParserConfiguration != null &&
447449
jsonParserConfiguration.isStrictMode() &&
448450
c == '\'') {
449-
throw this.syntaxError("Single quote wrap not allowed in strict mode");
451+
throw this.syntaxError("Strict mode error: Single quoted strings are not allowed");
450452
}
451453
switch (c) {
452454
case '"':
@@ -477,10 +479,11 @@ Object nextSimpleValue(char c) {
477479
throw this.syntaxError("Missing value");
478480
}
479481
Object obj = JSONObject.stringToValue(string);
482+
// Strict mode only allows strings with explicit double quotes
480483
if (jsonParserConfiguration != null &&
481484
jsonParserConfiguration.isStrictMode() &&
482485
obj instanceof String) {
483-
throw this.syntaxError(String.format("Value '%s' is not surrounded by quotes", obj));
486+
throw this.syntaxError(String.format("Strict mode error: Value '%s' is not surrounded by quotes", obj));
484487
}
485488
return obj;
486489
}

src/test/java/org/json/junit/JSONArrayTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -481,7 +481,7 @@ public void unquotedText() {
481481
System.out.println("Skipping JSONArrayTest unquotedText() when strictMode default is true");
482482
} else {
483483
String str = "[value1, something!, (parens), [email protected], 23, 23+45]";
484-
JSONArray jsonArray = new JSONArray(str);
484+
JSONArray jsonArray = new JSONArray(str);
485485
List<Object> expected = Arrays.asList("value1", "something!", "(parens)", "[email protected]", 23, "23+45");
486486
assertEquals(expected, jsonArray.toList());
487487
}

src/test/java/org/json/junit/JSONObjectTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ public void tearDown() {
8383
Singleton.getInstance().setSomeInt(0);
8484
Singleton.getInstance().setSomeString(null);
8585
}
86-
86+
8787
/**
8888
* Tests that the similar method is working as expected.
8989
*/

src/test/java/org/json/junit/JSONParserConfigurationTest.java

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,8 @@ public void givenInvalidStringArray_testStrictModeTrue_shouldThrowJsonException(
184184
.withStrictMode(true);
185185
String testCase = "[badString]";
186186
JSONException je = assertThrows(JSONException.class, () -> new JSONArray(testCase, jsonParserConfiguration));
187-
assertEquals("Value 'badString' is not surrounded by quotes at 10 [character 11 line 1]", je.getMessage());
187+
assertEquals("Strict mode error: Value 'badString' is not surrounded by quotes at 10 [character 11 line 1]",
188+
je.getMessage());
188189
}
189190

190191
@Test
@@ -193,7 +194,8 @@ public void givenInvalidStringObject_testStrictModeTrue_shouldThrowJsonException
193194
.withStrictMode(true);
194195
String testCase = "{\"a0\":badString}";
195196
JSONException je = assertThrows(JSONException.class, () -> new JSONObject(testCase, jsonParserConfiguration));
196-
assertEquals("Value 'badString' is not surrounded by quotes at 15 [character 16 line 1]", je.getMessage());
197+
assertEquals("Strict mode error: Value 'badString' is not surrounded by quotes at 15 [character 16 line 1]",
198+
je.getMessage());
197199
}
198200

199201
@Test
@@ -289,7 +291,8 @@ public void givenInvalidInputArray_testStrictModeTrue_shouldThrowInvalidCharacte
289291
String testCase = "[1,2];[3,4]";
290292
JSONException je = assertThrows("expected non-compliant array but got instead: " + testCase,
291293
JSONException.class, () -> new JSONArray(testCase, jsonParserConfiguration));
292-
assertEquals("invalid character ';' found after end of array at 6 [character 7 line 1]", je.getMessage());
294+
assertEquals("Strict mode error: Unparsed characters found at end of input text at 6 [character 7 line 1]",
295+
je.getMessage());
293296
}
294297

295298
@Test
@@ -299,7 +302,7 @@ public void givenInvalidInputObject_testStrictModeTrue_shouldThrowInvalidCharact
299302
String testCase = "{\"a0\":[1,2];\"a1\":[3,4]}";
300303
JSONException je = assertThrows("expected non-compliant array but got instead: " + testCase,
301304
JSONException.class, () -> new JSONObject(testCase, jsonParserConfiguration));
302-
assertEquals("Invalid character ';' found in object in strict mode at 12 [character 13 line 1]", je.getMessage());
305+
assertEquals("Strict mode error: Invalid character ';' found at 12 [character 13 line 1]", je.getMessage());
303306
}
304307

305308
@Test
@@ -309,7 +312,8 @@ public void givenInvalidInputArrayWithNumericStrings_testStrictModeTrue_shouldTh
309312
String testCase = "[\"1\",\"2\"];[3,4]";
310313
JSONException je = assertThrows("expected non-compliant array but got instead: " + testCase,
311314
JSONException.class, () -> new JSONArray(testCase, jsonParserConfiguration));
312-
assertEquals("invalid character ';' found after end of array at 10 [character 11 line 1]", je.getMessage());
315+
assertEquals("Strict mode error: Unparsed characters found at end of input text at 10 [character 11 line 1]",
316+
je.getMessage());
313317
}
314318

315319
@Test
@@ -319,7 +323,7 @@ public void givenInvalidInputObjectWithNumericStrings_testStrictModeTrue_shouldT
319323
String testCase = "{\"a0\":[\"1\",\"2\"];\"a1\":[3,4]}";
320324
JSONException je = assertThrows("expected non-compliant array but got instead: " + testCase,
321325
JSONException.class, () -> new JSONObject(testCase, jsonParserConfiguration));
322-
assertEquals("Invalid character ';' found in object in strict mode at 16 [character 17 line 1]", je.getMessage());
326+
assertEquals("Strict mode error: Invalid character ';' found at 16 [character 17 line 1]", je.getMessage());
323327
}
324328

325329
@Test
@@ -329,7 +333,8 @@ public void givenInvalidInputArray_testStrictModeTrue_shouldThrowValueNotSurroun
329333
String testCase = "[{\"test\": implied}]";
330334
JSONException je = assertThrows("expected non-compliant array but got instead: " + testCase,
331335
JSONException.class, () -> new JSONArray(testCase, jsonParserConfiguration));
332-
assertEquals("Value 'implied' is not surrounded by quotes at 17 [character 18 line 1]", je.getMessage());
336+
assertEquals("Strict mode error: Value 'implied' is not surrounded by quotes at 17 [character 18 line 1]",
337+
je.getMessage());
333338
}
334339

335340
@Test
@@ -339,7 +344,8 @@ public void givenInvalidInputObject_testStrictModeTrue_shouldThrowValueNotSurrou
339344
String testCase = "{\"a0\":{\"test\": implied}]}";
340345
JSONException je = assertThrows("expected non-compliant array but got instead: " + testCase,
341346
JSONException.class, () -> new JSONObject(testCase, jsonParserConfiguration));
342-
assertEquals("Value 'implied' is not surrounded by quotes at 22 [character 23 line 1]", je.getMessage());
347+
assertEquals("Strict mode error: Value 'implied' is not surrounded by quotes at 22 [character 23 line 1]",
348+
je.getMessage());
343349
}
344350

345351
@Test
@@ -381,13 +387,13 @@ public void givenNonCompliantQuotesArray_testStrictModeTrue_shouldThrowJsonExcep
381387
"Expected a ',' or ']' at 10 [character 11 line 1]",
382388
jeOne.getMessage());
383389
assertEquals(
384-
"Single quote wrap not allowed in strict mode at 2 [character 3 line 1]",
390+
"Strict mode error: Single quoted strings are not allowed at 2 [character 3 line 1]",
385391
jeTwo.getMessage());
386392
assertEquals(
387-
"Single quote wrap not allowed in strict mode at 2 [character 3 line 1]",
393+
"Strict mode error: Single quoted strings are not allowed at 2 [character 3 line 1]",
388394
jeThree.getMessage());
389395
assertEquals(
390-
"Single quote wrap not allowed in strict mode at 3 [character 4 line 1]",
396+
"Strict mode error: Single quoted strings are not allowed at 3 [character 4 line 1]",
391397
jeFour.getMessage());
392398
}
393399

@@ -414,13 +420,13 @@ public void givenNonCompliantQuotesObject_testStrictModeTrue_shouldThrowJsonExce
414420
"Expected a ':' after a key at 10 [character 11 line 1]",
415421
jeOne.getMessage());
416422
assertEquals(
417-
"Single quote wrap not allowed in strict mode at 2 [character 3 line 1]",
423+
"Strict mode error: Single quoted strings are not allowed at 2 [character 3 line 1]",
418424
jeTwo.getMessage());
419425
assertEquals(
420-
"Single quote wrap not allowed in strict mode at 6 [character 7 line 1]",
426+
"Strict mode error: Single quoted strings are not allowed at 6 [character 7 line 1]",
421427
jeThree.getMessage());
422428
assertEquals(
423-
"Single quote wrap not allowed in strict mode at 2 [character 3 line 1]",
429+
"Strict mode error: Single quoted strings are not allowed at 2 [character 3 line 1]",
424430
jeFour.getMessage());
425431
}
426432

@@ -467,7 +473,8 @@ public void givenInvalidInputArray_testStrictModeTrue_shouldThrowKeyNotSurrounde
467473
JSONException je = assertThrows("expected non-compliant array but got instead: " + testCase,
468474
JSONException.class, () -> new JSONArray(testCase, jsonParserConfiguration));
469475

470-
assertEquals("Value 'test' is not surrounded by quotes at 6 [character 7 line 1]", je.getMessage());
476+
assertEquals("Strict mode error: Value 'test' is not surrounded by quotes at 6 [character 7 line 1]",
477+
je.getMessage());
471478
}
472479

473480
@Test
@@ -479,7 +486,8 @@ public void givenInvalidInputObject_testStrictModeTrue_shouldThrowKeyNotSurround
479486
JSONException je = assertThrows("expected non-compliant json but got instead: " + testCase,
480487
JSONException.class, () -> new JSONObject(testCase, jsonParserConfiguration));
481488

482-
assertEquals("Value 'test' is not surrounded by quotes at 5 [character 6 line 1]", je.getMessage());
489+
assertEquals("Strict mode error: Value 'test' is not surrounded by quotes at 5 [character 6 line 1]",
490+
je.getMessage());
483491
}
484492

485493
/**
@@ -492,6 +500,8 @@ private List<String> getNonCompliantJSONArrayList() {
492500
return Arrays.asList(
493501
"[1],",
494502
"[1,]",
503+
"[,]",
504+
"[,,]",
495505
"[[1],\"sa\",[2]]a",
496506
"[1],\"dsa\": \"test\"",
497507
"[[a]]",

0 commit comments

Comments
 (0)