JSON Small and Fast Parser - A lightweight, high-performance JSON processing library for Java
—
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.
JSONParser offers fine-grained control over parsing behavior through various modes and flags.
public static final int ACCEPT_SIMPLE_QUOTE = 1;
public static final int ACCEPT_NON_QUOTE = 2;
public static final int ACCEPT_NAN = 4;
public static final int IGNORE_CONTROL_CHAR = 8;
public static final int USE_INTEGER_STORAGE = 16;
public static final int ACCEPT_LEADING_ZERO = 32;
public static final int ACCEPT_USELESS_COMMA = 64;
public static final int USE_HI_PRECISION_FLOAT = 128;
public static final int ACCEPT_TAILLING_DATA = 256;
public static final int ACCEPT_TAILLING_SPACE = 512;
public static final int REJECT_127_CHAR = 1024;
public static final int BIG_DIGIT_UNRESTRICTED = 2048;
public static final int LIMIT_JSON_DEPTH = 4096;
public static final int ACCEPT_INCOMPLETE = 8192;Individual flags that control specific parsing behaviors:
'hello'){key: value})NaN and Infinity valuesint instead of long when possible007)[1,2,3,])BigDecimal for floating-point numbersdouble for large numberspublic static final int MODE_PERMISSIVE;
public static final int MODE_PERMISSIVE_WITH_INCOMPLETE;
public static final int MODE_RFC4627;
public static final int MODE_JSON_SIMPLE;
public static final int MODE_STRICTEST;
public static int DEFAULT_PERMISSIVE_MODE;Predefined combinations of flags for common use cases:
public JSONParser();
public JSONParser(int permissiveMode);Create parser instances with default or custom modes.
// Default permissive parser
JSONParser parser = new JSONParser();
// Strict RFC4627 parser
JSONParser strictParser = new JSONParser(JSONParser.MODE_RFC4627);
// Custom mode with specific flags
int customMode = JSONParser.ACCEPT_SIMPLE_QUOTE | JSONParser.ACCEPT_USELESS_COMMA;
JSONParser customParser = new JSONParser(customMode);
// Streaming parser
JSONParser streamParser = new JSONParser(JSONParser.MODE_PERMISSIVE_WITH_INCOMPLETE);public Object parse(String in) throws ParseException;
public Object parse(Reader in) throws ParseException, IOException;
public Object parse(InputStream in) throws ParseException, IOException;
public Object parse(byte[] in) throws ParseException;Parse JSON from various input sources.
JSONParser parser = new JSONParser(JSONParser.MODE_PERMISSIVE);
// Parse string with single quotes (permissive mode)
Object obj1 = parser.parse("{'name': 'John', 'age': 30}");
// Parse with trailing comma (permissive mode)
Object obj2 = parser.parse("[1, 2, 3,]");
// Strict parser rejects malformed JSON
JSONParser strict = new JSONParser(JSONParser.MODE_RFC4627);
try {
strict.parse("{'name': 'John'}"); // Throws ParseException
} catch (ParseException e) {
System.out.println("Strict parsing failed: " + e.getMessage());
}public Object parse(String in, JsonReaderI<?> mapper) throws ParseException;
public <T> T parse(String in, JsonReaderI<T> mapper) throws ParseException;
public <T> T parse(Reader in, JsonReaderI<T> mapper) throws ParseException, IOException;
public <T> T parse(InputStream in, JsonReaderI<T> mapper) throws ParseException, IOException;
public <T> T parse(byte[] in, JsonReaderI<T> mapper) throws ParseException;Parse with custom deserialization logic.
// Custom mapper for dates
JsonReaderI<LocalDate> dateMapper = new JsonReaderI<LocalDate>(new JsonReader()) {
@Override
public LocalDate convert(Object current) {
if (current instanceof String) {
return LocalDate.parse((String) current);
}
return null;
}
};
JSONParser parser = new JSONParser();
LocalDate date = parser.parse("\"2023-10-15\"", dateMapper);JSONParser permissive = new JSONParser(JSONParser.MODE_PERMISSIVE);
// Single quotes
Object obj1 = permissive.parse("{'name': 'John'}");
// Unquoted keys
Object obj2 = permissive.parse("{name: 'John'}");
// Trailing commas
Object obj3 = permissive.parse("[1, 2, 3,]");
Object obj4 = permissive.parse("{'a': 1, 'b': 2,}");
// Leading zeros
Object obj5 = permissive.parse("{\"count\": 007}");
// Special numbers
Object obj6 = permissive.parse("{\"value\": NaN, \"max\": Infinity}");
// Extra data after JSON (ignored)
Object obj7 = permissive.parse("{\"valid\": true} extra data here");JSONParser strict = new JSONParser(JSONParser.MODE_RFC4627);
// Valid RFC4627 JSON
Object valid = strict.parse("{\"name\": \"John\", \"age\": 30}");
// These will throw ParseException in strict mode:
try {
strict.parse("{'name': 'John'}"); // Single quotes not allowed
strict.parse("{name: 'John'}"); // Unquoted keys not allowed
strict.parse("[1, 2, 3,]"); // Trailing commas not allowed
strict.parse("{\"count\": 007}"); // Leading zeros not allowed
strict.parse("{\"value\": NaN}"); // NaN not allowed
} catch (ParseException e) {
System.out.println("Strict parsing error: " + e.getMessage());
}// Create custom mode for specific needs
int webApiMode = JSONParser.ACCEPT_SIMPLE_QUOTE | // Allow single quotes
JSONParser.ACCEPT_USELESS_COMMA | // Allow trailing commas
JSONParser.USE_HI_PRECISION_FLOAT | // Use BigDecimal
JSONParser.ACCEPT_TAILLING_SPACE; // Allow trailing spaces
JSONParser webParser = new JSONParser(webApiMode);
// High precision numbers
Object result = webParser.parse("{'price': 123.456789012345678901234567890,}");
JSONObject obj = (JSONObject) result;
BigDecimal price = (BigDecimal) obj.get("price"); // Maintains full precisionMultipleJsonParser handles multiple JSON values in a single input stream, separated by whitespace.
public MultipleJsonParser(String in, int permissiveMode);
public MultipleJsonParser(Reader in, int permissiveMode);
public MultipleJsonParser(InputStream in, int permissiveMode);
public MultipleJsonParser(byte[] in, int permissiveMode);Create streaming parsers for various input sources.
// Multiple JSON objects in string
String multiJson = """
{"name": "Alice"}
{"name": "Bob"}
{"name": "Charlie"}
[1, 2, 3]
"simple string"
42
true
""";
MultipleJsonParser multiParser = new MultipleJsonParser(multiJson, JSONParser.MODE_PERMISSIVE);
// Stream from file
FileReader reader = new FileReader("multi.json");
MultipleJsonParser fileParser = new MultipleJsonParser(reader, JSONParser.MODE_PERMISSIVE);public Object parseNext() throws ParseException;
public <T> T parseNext(JsonReaderI<T> mapper) throws ParseException;
public <T> T parseNext(Class<T> mapTo) throws ParseException;
public boolean hasNext();Parse multiple JSON values sequentially.
String multiJson = """
{"id": 1, "name": "Alice"}
{"id": 2, "name": "Bob"}
{"id": 3, "name": "Charlie"}
""";
MultipleJsonParser parser = new MultipleJsonParser(multiJson, JSONParser.MODE_PERMISSIVE);
// Process each JSON object
while (parser.hasNext()) {
JSONObject user = parser.parseNext(JSONObject.class);
int id = user.getAsNumber("id").intValue();
String name = user.getAsString("name");
System.out.println("User " + id + ": " + name);
}// Process large file with multiple JSON objects
try (FileInputStream fis = new FileInputStream("large-data.json")) {
MultipleJsonParser parser = new MultipleJsonParser(fis, JSONParser.MODE_PERMISSIVE);
int count = 0;
while (parser.hasNext()) {
Object obj = parser.parseNext();
// Process each object
if (obj instanceof JSONObject) {
JSONObject jsonObj = (JSONObject) obj;
// Handle object
} else if (obj instanceof JSONArray) {
JSONArray jsonArr = (JSONArray) obj;
// Handle array
}
count++;
if (count % 1000 == 0) {
System.out.println("Processed " + count + " objects");
}
}
}ParseException provides detailed information about parsing errors.
public static final int ERROR_UNEXPECTED_CHAR = 0;
public static final int ERROR_UNEXPECTED_TOKEN = 1;
public static final int ERROR_UNEXPECTED_EXCEPTION = 2;
public static final int ERROR_UNEXPECTED_EOF = 3;
public static final int ERROR_UNEXPECTED_UNICODE = 4;
public static final int ERROR_UNEXPECTED_DUPLICATE_KEY = 5;
public static final int ERROR_UNEXPECTED_LEADING_0 = 6;
public static final int ERROR_UNEXPECTED_JSON_DEPTH = 7;public ParseException(int position, int errorType, Object unexpectedObject);
public ParseException(int position, Throwable cause);
public int getErrorType();
public int getPosition();
public Object getUnexpectedObject();Handle parsing errors with detailed information.
JSONParser parser = new JSONParser(JSONParser.MODE_RFC4627);
try {
parser.parse("{'invalid': json}");
} catch (ParseException e) {
int errorType = e.getErrorType();
int position = e.getPosition();
Object unexpected = e.getUnexpectedObject();
switch (errorType) {
case ParseException.ERROR_UNEXPECTED_CHAR:
System.out.println("Unexpected character '" + unexpected + "' at position " + position);
break;
case ParseException.ERROR_UNEXPECTED_TOKEN:
System.out.println("Unexpected token '" + unexpected + "' at position " + position);
break;
case ParseException.ERROR_UNEXPECTED_EOF:
System.out.println("Unexpected end of input at position " + position);
break;
case ParseException.ERROR_UNEXPECTED_JSON_DEPTH:
System.out.println("JSON too deeply nested at position " + position);
break;
default:
System.out.println("Parse error: " + e.getMessage());
}
}// Process incomplete JSON streams (useful for real-time data)
int streamMode = JSONParser.MODE_PERMISSIVE | JSONParser.ACCEPT_INCOMPLETE;
JSONParser streamParser = new JSONParser(streamMode);
String incompleteJson = "{\"name\": \"John\", \"age\":"; // Incomplete
try {
Object result = streamParser.parse(incompleteJson);
// May return partial object or handle gracefully
} catch (ParseException e) {
if (e.getErrorType() == ParseException.ERROR_UNEXPECTED_EOF) {
System.out.println("Incomplete JSON detected, waiting for more data...");
}
}// Parse financial data requiring high precision
int precisionMode = JSONParser.MODE_PERMISSIVE | JSONParser.USE_HI_PRECISION_FLOAT;
JSONParser precisionParser = new JSONParser(precisionMode);
String financialJson = """
{
"account": "12345",
"balance": 123456.789012345678901234567890,
"transactions": [
{"amount": 0.000000000000000001, "type": "micro"},
{"amount": 999999999999999999999.99, "type": "large"}
]
}
""";
JSONObject account = precisionParser.parse(financialJson, JSONObject.class);
BigDecimal balance = (BigDecimal) account.get("balance");
System.out.println("Exact balance: " + balance.toPlainString());
JSONArray transactions = (JSONArray) account.get("transactions");
for (Object txObj : transactions) {
JSONObject tx = (JSONObject) txObj;
BigDecimal amount = (BigDecimal) tx.get("amount");
System.out.println("Transaction: " + amount.toPlainString());
}public class RobustJSONProcessor {
public List<Object> parseMultipleWithErrorRecovery(String input) {
List<Object> results = new ArrayList<>();
MultipleJsonParser parser = new MultipleJsonParser(input, JSONParser.MODE_PERMISSIVE);
while (parser.hasNext()) {
try {
Object obj = parser.parseNext();
results.add(obj);
} catch (ParseException e) {
// Log error and continue with next JSON object
System.err.println("Failed to parse JSON at position " + e.getPosition() +
": " + e.getMessage());
// Add error marker to results
JSONObject errorObj = new JSONObject()
.appendField("error", true)
.appendField("position", e.getPosition())
.appendField("message", e.getMessage());
results.add(errorObj);
}
}
return results;
}
}// Configure parser for maximum performance
int fastMode = JSONParser.MODE_PERMISSIVE |
JSONParser.USE_INTEGER_STORAGE | // Use int instead of long
JSONParser.BIG_DIGIT_UNRESTRICTED; // Use double for big numbers
JSONParser fastParser = new JSONParser(fastMode);
// Batch process large amounts of JSON data
List<String> jsonStrings = loadLargeJsonDataset();
List<Object> results = new ArrayList<>();
long startTime = System.currentTimeMillis();
for (String json : jsonStrings) {
try {
Object obj = fastParser.parse(json);
results.add(obj);
} catch (ParseException e) {
// Handle errors
}
}
long endTime = System.currentTimeMillis();
System.out.println("Processed " + jsonStrings.size() +
" JSON objects in " + (endTime - startTime) + "ms");public class JSONValidator {
private final JSONParser strictParser = new JSONParser(JSONParser.MODE_RFC4627);
private final JSONParser permissiveParser = new JSONParser(JSONParser.MODE_PERMISSIVE);
public ValidationResult validateAndSanitize(String json) {
// Try strict parsing first
try {
Object strictResult = strictParser.parse(json);
return new ValidationResult(true, strictResult, null);
} catch (ParseException strictError) {
// Try permissive parsing for recovery
try {
Object permissiveResult = permissiveParser.parse(json);
// Re-serialize to get clean JSON
String cleanJson = JSONValue.toJSONString(permissiveResult);
Object cleanResult = strictParser.parse(cleanJson);
return new ValidationResult(false, cleanResult,
"JSON was sanitized: " + strictError.getMessage());
} catch (ParseException permissiveError) {
return new ValidationResult(false, null,
"JSON is invalid: " + permissiveError.getMessage());
}
}
}
public static class ValidationResult {
public final boolean isStrictlyValid;
public final Object result;
public final String message;
public ValidationResult(boolean isStrictlyValid, Object result, String message) {
this.isStrictlyValid = isStrictlyValid;
this.result = result;
this.message = message;
}
}
}Install with Tessl CLI
npx tessl i tessl/maven-net-minidev--json-smart