0
# Advanced JSON Parsing
1
2
JSON-Smart provides powerful and configurable parsing capabilities through the JSONParser class and MultipleJsonParser for streaming scenarios. This document covers advanced parsing features, configuration options, and error handling.
3
4
## JSONParser - Configurable Parsing
5
6
JSONParser offers fine-grained control over parsing behavior through various modes and flags.
7
8
### Parser Mode Constants
9
10
#### Parsing Flags
11
12
```java { .api }
13
public static final int ACCEPT_SIMPLE_QUOTE = 1;
14
public static final int ACCEPT_NON_QUOTE = 2;
15
public static final int ACCEPT_NAN = 4;
16
public static final int IGNORE_CONTROL_CHAR = 8;
17
public static final int USE_INTEGER_STORAGE = 16;
18
public static final int ACCEPT_LEADING_ZERO = 32;
19
public static final int ACCEPT_USELESS_COMMA = 64;
20
public static final int USE_HI_PRECISION_FLOAT = 128;
21
public static final int ACCEPT_TAILLING_DATA = 256;
22
public static final int ACCEPT_TAILLING_SPACE = 512;
23
public static final int REJECT_127_CHAR = 1024;
24
public static final int BIG_DIGIT_UNRESTRICTED = 2048;
25
public static final int LIMIT_JSON_DEPTH = 4096;
26
public static final int ACCEPT_INCOMPLETE = 8192;
27
```
28
29
Individual flags that control specific parsing behaviors:
30
31
- **ACCEPT_SIMPLE_QUOTE**: Allow single quotes for strings (`'hello'`)
32
- **ACCEPT_NON_QUOTE**: Allow unquoted strings (`{key: value}`)
33
- **ACCEPT_NAN**: Parse `NaN` and `Infinity` values
34
- **IGNORE_CONTROL_CHAR**: Ignore control characters in strings
35
- **USE_INTEGER_STORAGE**: Use `int` instead of `long` when possible
36
- **ACCEPT_LEADING_ZERO**: Allow numbers with leading zeros (`007`)
37
- **ACCEPT_USELESS_COMMA**: Allow trailing commas (`[1,2,3,]`)
38
- **USE_HI_PRECISION_FLOAT**: Use `BigDecimal` for floating-point numbers
39
- **ACCEPT_TAILLING_DATA**: Allow extra data after JSON
40
- **ACCEPT_TAILLING_SPACE**: Allow trailing whitespace
41
- **REJECT_127_CHAR**: Reject ASCII character 127
42
- **BIG_DIGIT_UNRESTRICTED**: Use `double` for large numbers
43
- **LIMIT_JSON_DEPTH**: Limit nesting depth (default enabled)
44
- **ACCEPT_INCOMPLETE**: Support streaming/incomplete JSON
45
46
#### Preset Modes
47
48
```java { .api }
49
public static final int MODE_PERMISSIVE;
50
public static final int MODE_PERMISSIVE_WITH_INCOMPLETE;
51
public static final int MODE_RFC4627;
52
public static final int MODE_JSON_SIMPLE;
53
public static final int MODE_STRICTEST;
54
public static int DEFAULT_PERMISSIVE_MODE;
55
```
56
57
Predefined combinations of flags for common use cases:
58
59
- **MODE_PERMISSIVE**: Fast, flexible parsing (default)
60
- **MODE_PERMISSIVE_WITH_INCOMPLETE**: Permissive + streaming support
61
- **MODE_RFC4627**: Strict RFC4627 compliance
62
- **MODE_JSON_SIMPLE**: Compatibility with json-simple library
63
- **MODE_STRICTEST**: Most restrictive parsing
64
65
### Constructors
66
67
```java { .api }
68
public JSONParser();
69
public JSONParser(int permissiveMode);
70
```
71
72
Create parser instances with default or custom modes.
73
74
```java
75
// Default permissive parser
76
JSONParser parser = new JSONParser();
77
78
// Strict RFC4627 parser
79
JSONParser strictParser = new JSONParser(JSONParser.MODE_RFC4627);
80
81
// Custom mode with specific flags
82
int customMode = JSONParser.ACCEPT_SIMPLE_QUOTE | JSONParser.ACCEPT_USELESS_COMMA;
83
JSONParser customParser = new JSONParser(customMode);
84
85
// Streaming parser
86
JSONParser streamParser = new JSONParser(JSONParser.MODE_PERMISSIVE_WITH_INCOMPLETE);
87
```
88
89
### Parsing Methods
90
91
#### Basic Parsing
92
93
```java { .api }
94
public Object parse(String in) throws ParseException;
95
public Object parse(Reader in) throws ParseException, IOException;
96
public Object parse(InputStream in) throws ParseException, IOException;
97
public Object parse(byte[] in) throws ParseException;
98
```
99
100
Parse JSON from various input sources.
101
102
```java
103
JSONParser parser = new JSONParser(JSONParser.MODE_PERMISSIVE);
104
105
// Parse string with single quotes (permissive mode)
106
Object obj1 = parser.parse("{'name': 'John', 'age': 30}");
107
108
// Parse with trailing comma (permissive mode)
109
Object obj2 = parser.parse("[1, 2, 3,]");
110
111
// Strict parser rejects malformed JSON
112
JSONParser strict = new JSONParser(JSONParser.MODE_RFC4627);
113
try {
114
strict.parse("{'name': 'John'}"); // Throws ParseException
115
} catch (ParseException e) {
116
System.out.println("Strict parsing failed: " + e.getMessage());
117
}
118
```
119
120
#### Parsing with Custom Mappers
121
122
```java { .api }
123
public Object parse(String in, JsonReaderI<?> mapper) throws ParseException;
124
public <T> T parse(String in, JsonReaderI<T> mapper) throws ParseException;
125
public <T> T parse(Reader in, JsonReaderI<T> mapper) throws ParseException, IOException;
126
public <T> T parse(InputStream in, JsonReaderI<T> mapper) throws ParseException, IOException;
127
public <T> T parse(byte[] in, JsonReaderI<T> mapper) throws ParseException;
128
```
129
130
Parse with custom deserialization logic.
131
132
```java
133
// Custom mapper for dates
134
JsonReaderI<LocalDate> dateMapper = new JsonReaderI<LocalDate>(new JsonReader()) {
135
@Override
136
public LocalDate convert(Object current) {
137
if (current instanceof String) {
138
return LocalDate.parse((String) current);
139
}
140
return null;
141
}
142
};
143
144
JSONParser parser = new JSONParser();
145
LocalDate date = parser.parse("\"2023-10-15\"", dateMapper);
146
```
147
148
## Parser Mode Examples
149
150
### Permissive Mode Features
151
152
```java
153
JSONParser permissive = new JSONParser(JSONParser.MODE_PERMISSIVE);
154
155
// Single quotes
156
Object obj1 = permissive.parse("{'name': 'John'}");
157
158
// Unquoted keys
159
Object obj2 = permissive.parse("{name: 'John'}");
160
161
// Trailing commas
162
Object obj3 = permissive.parse("[1, 2, 3,]");
163
Object obj4 = permissive.parse("{'a': 1, 'b': 2,}");
164
165
// Leading zeros
166
Object obj5 = permissive.parse("{\"count\": 007}");
167
168
// Special numbers
169
Object obj6 = permissive.parse("{\"value\": NaN, \"max\": Infinity}");
170
171
// Extra data after JSON (ignored)
172
Object obj7 = permissive.parse("{\"valid\": true} extra data here");
173
```
174
175
### Strict RFC4627 Mode
176
177
```java
178
JSONParser strict = new JSONParser(JSONParser.MODE_RFC4627);
179
180
// Valid RFC4627 JSON
181
Object valid = strict.parse("{\"name\": \"John\", \"age\": 30}");
182
183
// These will throw ParseException in strict mode:
184
try {
185
strict.parse("{'name': 'John'}"); // Single quotes not allowed
186
strict.parse("{name: 'John'}"); // Unquoted keys not allowed
187
strict.parse("[1, 2, 3,]"); // Trailing commas not allowed
188
strict.parse("{\"count\": 007}"); // Leading zeros not allowed
189
strict.parse("{\"value\": NaN}"); // NaN not allowed
190
} catch (ParseException e) {
191
System.out.println("Strict parsing error: " + e.getMessage());
192
}
193
```
194
195
### Custom Mode Configuration
196
197
```java
198
// Create custom mode for specific needs
199
int webApiMode = JSONParser.ACCEPT_SIMPLE_QUOTE | // Allow single quotes
200
JSONParser.ACCEPT_USELESS_COMMA | // Allow trailing commas
201
JSONParser.USE_HI_PRECISION_FLOAT | // Use BigDecimal
202
JSONParser.ACCEPT_TAILLING_SPACE; // Allow trailing spaces
203
204
JSONParser webParser = new JSONParser(webApiMode);
205
206
// High precision numbers
207
Object result = webParser.parse("{'price': 123.456789012345678901234567890,}");
208
JSONObject obj = (JSONObject) result;
209
BigDecimal price = (BigDecimal) obj.get("price"); // Maintains full precision
210
```
211
212
## MultipleJsonParser - Streaming Support
213
214
MultipleJsonParser handles multiple JSON values in a single input stream, separated by whitespace.
215
216
### Constructors
217
218
```java { .api }
219
public MultipleJsonParser(String in, int permissiveMode);
220
public MultipleJsonParser(Reader in, int permissiveMode);
221
public MultipleJsonParser(InputStream in, int permissiveMode);
222
public MultipleJsonParser(byte[] in, int permissiveMode);
223
```
224
225
Create streaming parsers for various input sources.
226
227
```java
228
// Multiple JSON objects in string
229
String multiJson = """
230
{"name": "Alice"}
231
{"name": "Bob"}
232
{"name": "Charlie"}
233
[1, 2, 3]
234
"simple string"
235
42
236
true
237
""";
238
239
MultipleJsonParser multiParser = new MultipleJsonParser(multiJson, JSONParser.MODE_PERMISSIVE);
240
241
// Stream from file
242
FileReader reader = new FileReader("multi.json");
243
MultipleJsonParser fileParser = new MultipleJsonParser(reader, JSONParser.MODE_PERMISSIVE);
244
```
245
246
### Parsing Methods
247
248
```java { .api }
249
public Object parseNext() throws ParseException;
250
public <T> T parseNext(JsonReaderI<T> mapper) throws ParseException;
251
public <T> T parseNext(Class<T> mapTo) throws ParseException;
252
public boolean hasNext();
253
```
254
255
Parse multiple JSON values sequentially.
256
257
```java
258
String multiJson = """
259
{"id": 1, "name": "Alice"}
260
{"id": 2, "name": "Bob"}
261
{"id": 3, "name": "Charlie"}
262
""";
263
264
MultipleJsonParser parser = new MultipleJsonParser(multiJson, JSONParser.MODE_PERMISSIVE);
265
266
// Process each JSON object
267
while (parser.hasNext()) {
268
JSONObject user = parser.parseNext(JSONObject.class);
269
int id = user.getAsNumber("id").intValue();
270
String name = user.getAsString("name");
271
System.out.println("User " + id + ": " + name);
272
}
273
```
274
275
### Streaming File Processing
276
277
```java
278
// Process large file with multiple JSON objects
279
try (FileInputStream fis = new FileInputStream("large-data.json")) {
280
MultipleJsonParser parser = new MultipleJsonParser(fis, JSONParser.MODE_PERMISSIVE);
281
282
int count = 0;
283
while (parser.hasNext()) {
284
Object obj = parser.parseNext();
285
286
// Process each object
287
if (obj instanceof JSONObject) {
288
JSONObject jsonObj = (JSONObject) obj;
289
// Handle object
290
} else if (obj instanceof JSONArray) {
291
JSONArray jsonArr = (JSONArray) obj;
292
// Handle array
293
}
294
295
count++;
296
if (count % 1000 == 0) {
297
System.out.println("Processed " + count + " objects");
298
}
299
}
300
}
301
```
302
303
## ParseException - Error Handling
304
305
ParseException provides detailed information about parsing errors.
306
307
### Error Type Constants
308
309
```java { .api }
310
public static final int ERROR_UNEXPECTED_CHAR = 0;
311
public static final int ERROR_UNEXPECTED_TOKEN = 1;
312
public static final int ERROR_UNEXPECTED_EXCEPTION = 2;
313
public static final int ERROR_UNEXPECTED_EOF = 3;
314
public static final int ERROR_UNEXPECTED_UNICODE = 4;
315
public static final int ERROR_UNEXPECTED_DUPLICATE_KEY = 5;
316
public static final int ERROR_UNEXPECTED_LEADING_0 = 6;
317
public static final int ERROR_UNEXPECTED_JSON_DEPTH = 7;
318
```
319
320
### Constructors and Methods
321
322
```java { .api }
323
public ParseException(int position, int errorType, Object unexpectedObject);
324
public ParseException(int position, Throwable cause);
325
326
public int getErrorType();
327
public int getPosition();
328
public Object getUnexpectedObject();
329
```
330
331
Handle parsing errors with detailed information.
332
333
```java
334
JSONParser parser = new JSONParser(JSONParser.MODE_RFC4627);
335
336
try {
337
parser.parse("{'invalid': json}");
338
} catch (ParseException e) {
339
int errorType = e.getErrorType();
340
int position = e.getPosition();
341
Object unexpected = e.getUnexpectedObject();
342
343
switch (errorType) {
344
case ParseException.ERROR_UNEXPECTED_CHAR:
345
System.out.println("Unexpected character '" + unexpected + "' at position " + position);
346
break;
347
case ParseException.ERROR_UNEXPECTED_TOKEN:
348
System.out.println("Unexpected token '" + unexpected + "' at position " + position);
349
break;
350
case ParseException.ERROR_UNEXPECTED_EOF:
351
System.out.println("Unexpected end of input at position " + position);
352
break;
353
case ParseException.ERROR_UNEXPECTED_JSON_DEPTH:
354
System.out.println("JSON too deeply nested at position " + position);
355
break;
356
default:
357
System.out.println("Parse error: " + e.getMessage());
358
}
359
}
360
```
361
362
## Advanced Parsing Examples
363
364
### Incomplete JSON Processing
365
366
```java
367
// Process incomplete JSON streams (useful for real-time data)
368
int streamMode = JSONParser.MODE_PERMISSIVE | JSONParser.ACCEPT_INCOMPLETE;
369
JSONParser streamParser = new JSONParser(streamMode);
370
371
String incompleteJson = "{\"name\": \"John\", \"age\":"; // Incomplete
372
try {
373
Object result = streamParser.parse(incompleteJson);
374
// May return partial object or handle gracefully
375
} catch (ParseException e) {
376
if (e.getErrorType() == ParseException.ERROR_UNEXPECTED_EOF) {
377
System.out.println("Incomplete JSON detected, waiting for more data...");
378
}
379
}
380
```
381
382
### High-Precision Number Handling
383
384
```java
385
// Parse financial data requiring high precision
386
int precisionMode = JSONParser.MODE_PERMISSIVE | JSONParser.USE_HI_PRECISION_FLOAT;
387
JSONParser precisionParser = new JSONParser(precisionMode);
388
389
String financialJson = """
390
{
391
"account": "12345",
392
"balance": 123456.789012345678901234567890,
393
"transactions": [
394
{"amount": 0.000000000000000001, "type": "micro"},
395
{"amount": 999999999999999999999.99, "type": "large"}
396
]
397
}
398
""";
399
400
JSONObject account = precisionParser.parse(financialJson, JSONObject.class);
401
BigDecimal balance = (BigDecimal) account.get("balance");
402
System.out.println("Exact balance: " + balance.toPlainString());
403
404
JSONArray transactions = (JSONArray) account.get("transactions");
405
for (Object txObj : transactions) {
406
JSONObject tx = (JSONObject) txObj;
407
BigDecimal amount = (BigDecimal) tx.get("amount");
408
System.out.println("Transaction: " + amount.toPlainString());
409
}
410
```
411
412
### Custom Error Recovery
413
414
```java
415
public class RobustJSONProcessor {
416
417
public List<Object> parseMultipleWithErrorRecovery(String input) {
418
List<Object> results = new ArrayList<>();
419
MultipleJsonParser parser = new MultipleJsonParser(input, JSONParser.MODE_PERMISSIVE);
420
421
while (parser.hasNext()) {
422
try {
423
Object obj = parser.parseNext();
424
results.add(obj);
425
} catch (ParseException e) {
426
// Log error and continue with next JSON object
427
System.err.println("Failed to parse JSON at position " + e.getPosition() +
428
": " + e.getMessage());
429
430
// Add error marker to results
431
JSONObject errorObj = new JSONObject()
432
.appendField("error", true)
433
.appendField("position", e.getPosition())
434
.appendField("message", e.getMessage());
435
results.add(errorObj);
436
}
437
}
438
439
return results;
440
}
441
}
442
```
443
444
### Performance-Optimized Parsing
445
446
```java
447
// Configure parser for maximum performance
448
int fastMode = JSONParser.MODE_PERMISSIVE |
449
JSONParser.USE_INTEGER_STORAGE | // Use int instead of long
450
JSONParser.BIG_DIGIT_UNRESTRICTED; // Use double for big numbers
451
452
JSONParser fastParser = new JSONParser(fastMode);
453
454
// Batch process large amounts of JSON data
455
List<String> jsonStrings = loadLargeJsonDataset();
456
List<Object> results = new ArrayList<>();
457
458
long startTime = System.currentTimeMillis();
459
for (String json : jsonStrings) {
460
try {
461
Object obj = fastParser.parse(json);
462
results.add(obj);
463
} catch (ParseException e) {
464
// Handle errors
465
}
466
}
467
long endTime = System.currentTimeMillis();
468
469
System.out.println("Processed " + jsonStrings.size() +
470
" JSON objects in " + (endTime - startTime) + "ms");
471
```
472
473
### Validation and Sanitization
474
475
```java
476
public class JSONValidator {
477
478
private final JSONParser strictParser = new JSONParser(JSONParser.MODE_RFC4627);
479
private final JSONParser permissiveParser = new JSONParser(JSONParser.MODE_PERMISSIVE);
480
481
public ValidationResult validateAndSanitize(String json) {
482
// Try strict parsing first
483
try {
484
Object strictResult = strictParser.parse(json);
485
return new ValidationResult(true, strictResult, null);
486
} catch (ParseException strictError) {
487
488
// Try permissive parsing for recovery
489
try {
490
Object permissiveResult = permissiveParser.parse(json);
491
492
// Re-serialize to get clean JSON
493
String cleanJson = JSONValue.toJSONString(permissiveResult);
494
Object cleanResult = strictParser.parse(cleanJson);
495
496
return new ValidationResult(false, cleanResult,
497
"JSON was sanitized: " + strictError.getMessage());
498
499
} catch (ParseException permissiveError) {
500
return new ValidationResult(false, null,
501
"JSON is invalid: " + permissiveError.getMessage());
502
}
503
}
504
}
505
506
public static class ValidationResult {
507
public final boolean isStrictlyValid;
508
public final Object result;
509
public final String message;
510
511
public ValidationResult(boolean isStrictlyValid, Object result, String message) {
512
this.isStrictlyValid = isStrictlyValid;
513
this.result = result;
514
this.message = message;
515
}
516
}
517
}
518
```