or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

configuration.mdcore-api.mdcustomization.mdindex.mdnavigation.mdparsing.md

parsing.mddocs/

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

```