JSON library for the Apache Groovy programming language providing JSON parsing, generation, and manipulation capabilities
—
Token-based JSON processing for fine-grained control over parsing and custom JSON processing workflows.
Streaming JSON lexer that tokenizes JSON input for low-level processing and custom parsing scenarios.
/**
* Streaming JSON token lexer implementing Iterator<JsonToken>
*/
public class JsonLexer implements Iterator<JsonToken> {
/** Constructor with Reader input */
public JsonLexer(Reader reader);
/** Get the underlying LineColumnReader */
public LineColumnReader getReader();
/** Get the next JSON token */
public JsonToken nextToken();
/** Skip whitespace characters and return count */
public int skipWhitespace();
/** Iterator interface: check if more tokens available */
public boolean hasNext();
/** Iterator interface: get next token */
public JsonToken next();
/** Iterator interface: remove operation (throws UnsupportedOperationException) */
public void remove();
/** Unescape JSON string content */
public static String unescape(String input);
}Usage Examples:
import groovy.json.JsonLexer;
import groovy.json.JsonToken;
import groovy.json.JsonTokenType;
import java.io.StringReader;
// Basic tokenization
String jsonInput = '{"name":"Alice","age":30,"active":true}';
JsonLexer lexer = new JsonLexer(new StringReader(jsonInput));
while (lexer.hasNext()) {
JsonToken token = lexer.next();
System.out.println("Type: " + token.getType() +
", Value: " + token.getValue() +
", Position: " + token.getStartLine() + ":" + token.getStartColumn());
}
// Custom JSON validator
public boolean isValidJson(String json) {
try {
JsonLexer lexer = new JsonLexer(new StringReader(json));
int depth = 0;
boolean inObject = false, inArray = false;
while (lexer.hasNext()) {
JsonToken token = lexer.next();
switch (token.getType()) {
case OPEN_BRACE:
depth++;
inObject = true;
break;
case CLOSE_BRACE:
depth--;
if (depth < 0) return false;
break;
case OPEN_BRACKET:
depth++;
inArray = true;
break;
case CLOSE_BRACKET:
depth--;
if (depth < 0) return false;
break;
}
}
return depth == 0;
} catch (Exception e) {
return false;
}
}
// Extract specific values by path
public Object extractJsonValue(String json, String[] path) {
JsonLexer lexer = new JsonLexer(new StringReader(json));
// Implementation for path-based extraction
// ... custom logic using token stream
}Represents a single JSON token with value, type, and position information.
/**
* Represents a single JSON token with position and value information
*/
public class JsonToken {
/** Get the parsed value of the token (Boolean, Number, String, or null) */
public Object getValue();
/** Get the token type */
public JsonTokenType getType();
public void setType(JsonTokenType type);
/** Get/set the raw text content */
public String getText();
public void setText(String text);
/** Position information - line numbers */
public long getStartLine();
public void setStartLine(long startLine);
public long getEndLine();
public void setEndLine(long endLine);
/** Position information - column numbers */
public long getStartColumn();
public void setStartColumn(long startColumn);
public long getEndColumn();
public void setEndColumn(long endColumn);
/** String representation of the token */
public String toString();
}Usage Examples:
// Token inspection and debugging
JsonToken token = lexer.nextToken();
if (token.getType() == JsonTokenType.STRING) {
System.out.println("String token: '" + token.getValue() + "' at " +
token.getStartLine() + ":" + token.getStartColumn());
}
// Error reporting with position
if (token.getType() == JsonTokenType.STRING && !isValidEmail((String) token.getValue())) {
throw new ValidationException("Invalid email at line " + token.getStartLine() +
", column " + token.getStartColumn());
}Enumeration of all possible JSON token types with validation and matching capabilities.
/**
* Enumeration of all JSON token types
*/
public enum JsonTokenType {
/** Structural tokens */
OPEN_CURLY, // {
CLOSE_CURLY, // }
OPEN_BRACKET, // [
CLOSE_BRACKET, // ]
COLON, // :
COMMA, // ,
/** Literal value tokens */
NULL, // null
TRUE, // true
FALSE, // false
NUMBER, // numeric values
STRING, // string values
/** Check if input matches this token type pattern */
public boolean matching(String input);
/** Get descriptive label for this token type */
public String getLabel();
/** Get validator (String, Pattern, or Closure) */
public Object getValidator();
/** Find token type that starts with given character */
public static JsonTokenType startingWith(char c);
}Usage Examples:
// Token type checking
JsonToken token = lexer.nextToken();
switch (token.getType()) {
case OPEN_BRACE:
System.out.println("Starting object");
break;
case OPEN_BRACKET:
System.out.println("Starting array");
break;
case STRING:
System.out.println("String value: " + token.getValue());
break;
case NUMBER:
System.out.println("Numeric value: " + token.getValue());
break;
case TRUE:
case FALSE:
System.out.println("Boolean value: " + token.getValue());
break;
case NULL:
System.out.println("Null value");
break;
}
// Token type pattern matching
String input = "true";
if (JsonTokenType.TRUE.matching(input)) {
System.out.println("Input matches TRUE token");
}
// Find token type by starting character
JsonTokenType type = JsonTokenType.startingWith('{');
System.out.println("Token starting with '{': " + type); // OPEN_BRACEimport groovy.json.JsonLexer;
import groovy.json.JsonToken;
import groovy.json.JsonTokenType;
public class CustomJsonParser {
private JsonLexer lexer;
private JsonToken currentToken;
public CustomJsonParser(Reader reader) {
this.lexer = new JsonLexer(reader);
advance();
}
private void advance() {
currentToken = lexer.hasNext() ? lexer.next() : null;
}
private void expect(JsonTokenType expectedType) {
if (currentToken == null || currentToken.getType() != expectedType) {
throw new RuntimeException("Expected " + expectedType +
" but found " +
(currentToken != null ? currentToken.getType() : "EOF"));
}
advance();
}
public Object parseValue() {
if (currentToken == null) {
throw new RuntimeException("Unexpected end of input");
}
switch (currentToken.getType()) {
case TRUE:
advance();
return true;
case FALSE:
advance();
return false;
case NULL:
advance();
return null;
case STRING:
String stringValue = (String) currentToken.getValue();
advance();
return stringValue;
case NUMBER:
Number numberValue = (Number) currentToken.getValue();
advance();
return numberValue;
case OPEN_BRACE:
return parseObject();
case OPEN_BRACKET:
return parseArray();
default:
throw new RuntimeException("Unexpected token: " + currentToken.getType());
}
}
private Map<String, Object> parseObject() {
Map<String, Object> object = new LinkedHashMap<>();
expect(JsonTokenType.OPEN_BRACE);
if (currentToken != null && currentToken.getType() == JsonTokenType.CLOSE_BRACE) {
advance();
return object;
}
do {
if (currentToken == null || currentToken.getType() != JsonTokenType.STRING) {
throw new RuntimeException("Expected string key");
}
String key = (String) currentToken.getValue();
advance();
expect(JsonTokenType.COLON);
Object value = parseValue();
object.put(key, value);
if (currentToken != null && currentToken.getType() == JsonTokenType.COMMA) {
advance();
} else {
break;
}
} while (true);
expect(JsonTokenType.CLOSE_BRACE);
return object;
}
private List<Object> parseArray() {
List<Object> array = new ArrayList<>();
expect(JsonTokenType.OPEN_BRACKET);
if (currentToken != null && currentToken.getType() == JsonTokenType.CLOSE_BRACKET) {
advance();
return array;
}
do {
array.add(parseValue());
if (currentToken != null && currentToken.getType() == JsonTokenType.COMMA) {
advance();
} else {
break;
}
} while (true);
expect(JsonTokenType.CLOSE_BRACKET);
return array;
}
}// Process large JSON streams without loading entire document
public class JsonStreamProcessor {
public void processJsonStream(Reader reader, StreamHandler handler) {
JsonLexer lexer = new JsonLexer(reader);
Stack<String> path = new Stack<>();
while (lexer.hasNext()) {
JsonToken token = lexer.next();
switch (token.getType()) {
case OPEN_BRACE:
handler.startObject(getCurrentPath(path));
break;
case CLOSE_BRACE:
handler.endObject(getCurrentPath(path));
if (!path.isEmpty()) path.pop();
break;
case OPEN_BRACKET:
handler.startArray(getCurrentPath(path));
break;
case CLOSE_BRACKET:
handler.endArray(getCurrentPath(path));
if (!path.isEmpty()) path.pop();
break;
case STRING:
if (isPropertyName(lexer)) {
path.push((String) token.getValue());
} else {
handler.stringValue(getCurrentPath(path), (String) token.getValue());
}
break;
case NUMBER:
handler.numberValue(getCurrentPath(path), (Number) token.getValue());
break;
case TRUE:
case FALSE:
handler.booleanValue(getCurrentPath(path), (Boolean) token.getValue());
break;
case NULL:
handler.nullValue(getCurrentPath(path));
break;
}
}
}
private String getCurrentPath(Stack<String> path) {
return String.join(".", path);
}
interface StreamHandler {
void startObject(String path);
void endObject(String path);
void startArray(String path);
void endArray(String path);
void stringValue(String path, String value);
void numberValue(String path, Number value);
void booleanValue(String path, Boolean value);
void nullValue(String path);
}
}public class JsonValidator {
public static class ValidationError {
private final String message;
private final long line;
private final long column;
public ValidationError(String message, JsonToken token) {
this.message = message;
this.line = token.getStartLine();
this.column = token.getStartColumn();
}
public String getMessage() { return message; }
public long getLine() { return line; }
public long getColumn() { return column; }
@Override
public String toString() {
return String.format("%s at line %d, column %d", message, line, column);
}
}
public static List<ValidationError> validate(String json) {
List<ValidationError> errors = new ArrayList<>();
try {
JsonLexer lexer = new JsonLexer(new StringReader(json));
Stack<JsonTokenType> stack = new Stack<>();
while (lexer.hasNext()) {
JsonToken token = lexer.next();
switch (token.getType()) {
case OPEN_BRACE:
stack.push(JsonTokenType.OPEN_BRACE);
break;
case CLOSE_BRACE:
if (stack.isEmpty() || stack.pop() != JsonTokenType.OPEN_BRACE) {
errors.add(new ValidationError("Unmatched closing brace", token));
}
break;
case OPEN_BRACKET:
stack.push(JsonTokenType.OPEN_BRACKET);
break;
case CLOSE_BRACKET:
if (stack.isEmpty() || stack.pop() != JsonTokenType.OPEN_BRACKET) {
errors.add(new ValidationError("Unmatched closing bracket", token));
}
break;
}
}
if (!stack.isEmpty()) {
errors.add(new ValidationError("Unclosed " +
(stack.peek() == JsonTokenType.OPEN_BRACE ? "object" : "array"), null));
}
} catch (Exception e) {
errors.add(new ValidationError("Parse error: " + e.getMessage(), null));
}
return errors;
}
}// Process large JSON files without loading into memory
public void processLargeJsonFile(File jsonFile) throws IOException {
try (FileReader reader = new FileReader(jsonFile);
BufferedReader buffered = new BufferedReader(reader)) {
JsonLexer lexer = new JsonLexer(buffered);
// Process tokens as they're read
while (lexer.hasNext()) {
JsonToken token = lexer.next();
processToken(token);
// Periodically yield to prevent blocking
if (Thread.interrupted()) {
throw new InterruptedException("Processing interrupted");
}
}
}
}// Skip unwanted tokens for performance
public List<String> extractStringValues(String json) {
List<String> strings = new ArrayList<>();
JsonLexer lexer = new JsonLexer(new StringReader(json));
while (lexer.hasNext()) {
JsonToken token = lexer.next();
if (token.getType() == JsonTokenType.STRING) {
strings.add((String) token.getValue());
}
// Skip other token types for performance
}
return strings;
}Install with Tessl CLI
npx tessl i tessl/maven-org-apache-groovy--groovy-json