Class JsonTokenReader
- All Implemented Interfaces:
Closeable,AutoCloseable,RecordReader,TokenReader
- Direct Known Subclasses:
Json5TokenReader,JsonlTokenReader
TokenReader surface for the JSON format.
Backed by the existing internal ParserReader plumbing. Operates as a hand-rolled state
machine over the JSON grammar — structural tokens (
Pipe-level settings (charset, auto-close, unbuffered) are configured on the
ParserPipe the caller hands in. Cursor-level behavior is driven by a small
JsonTokenReader.Settings bundle — currently just
Notes:
- This class is not thread safe.
- Whitespace and JSON comments are tolerated — comments are silently skipped to mirror Juneau's existing JSON parser behavior.
- The cursor is purely structural; object swaps and
@Schemaannotations have no effect. Use the POJO databind path onJsonParserfor those.
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic final recordCursor-level settings honored byJsonTokenReader. -
Field Summary
FieldsModifier and TypeFieldDescriptionprotected booleanBoolean value buffer for the currentTokenType.VALUE_BOOLEANtoken.protected StringField name buffer for the currentTokenType.FIELD_NAMEtoken.protected StringNumeric lexeme buffer for the currentTokenType.VALUE_NUMBERtoken (preserved verbatim from the input).protected StringString value buffer for the currentTokenType.VALUE_STRINGtoken.protected TokenTypeThe token most recently emitted bynext().protected intCurrent container nesting depth (0 = root).protected final ParserReaderUnderlying character-level reader; subclasses use this for directread/unread/mark/getMarkedcalls during dialect-specific tokenization.protected static final intState-machine state: at root, after[ , after: , or after, inside an array.protected static final intState-machine state: after{ or after, inside an object.protected static final intState-machine state: just emitted aTokenType.FIELD_NAME.protected static final intState-machine state: just emitted a value inside an object.protected static final intState-machine state: just emitted a value inside an array.protected static final intState-machine state: the root document has been fully consumed.protected final JsonTokenReader.SettingsCursor-level settings (currently justtrimStrings ).protected intCurrent state-machine state; one of theSTATE_*constants.Fields inherited from interface org.apache.juneau.marshall.stream.TokenReader
MSG_READ_NON_VALUE_STATE -
Constructor Summary
ConstructorsConstructorDescriptionJsonTokenReader(ParserPipe pipe) Constructor with defaultJsonTokenReader.Settings.JsonTokenReader(ParserPipe pipe, JsonTokenReader.Settings settings) Constructor with explicitJsonTokenReader.Settings.JsonTokenReader(ParserPipe pipe, JsonTokenReader.Settings settings, JsonParserSession session) Constructor used byJsonParser.readTokens(Object)to plumb the calling parser's session in so thatread(Class)can delegate to the existing databind path. -
Method Summary
Modifier and TypeMethodDescriptionprotected voidSets the post-value state based on the enclosing container.protected booleanSubclass extension point: returns whether trailing commas are accepted before] /} .booleancanRead()Returnstrue ifTokenReader.read(Class)is currently callable on this cursor — i.e. the cursor is positioned at a value boundary.voidclose()Closes the underlying stream.byte[]Returns the current binary value.booleangetBool()Returns the current boolean value.Returns the current token type without advancing.intgetDepth()Returns the current depth of the cursor.Returns the field name when the current token isTokenType.FIELD_NAME.Returns the current numeric value as a parsedNumber.Returns the original numeric lexeme for the current numeric token.Returns the current scalar value as a string.protected static booleanisDigit(int c) Returns whether the given character is an ASCII decimal digit.protected booleanReturns whether the cursor's current container (the one most recently pushed) is an array.protected static booleanisNumberStart(int c) Returns whether the given character could start a JSON number lexeme (digit or minus sign).next()Advances the cursor to the next token.protected ParseExceptionparseException(String message, Object... args) protected voidPops the top container off the cursor's depth stack.protected voidpushContainer(boolean isArray) Pushes a new container onto the cursor's depth stack.<T> TConsumes the upcoming value (object, array, or scalar) and binds it to a POJO via the format's existing parser.<T> TSame asTokenReader.read(Class)but accepts a parameterizedTypeand optional type arguments (the same shape asParser.read(Object, Type, Type...)).<T> TSame asTokenReader.read(Class)but accepts a pre-resolvedClassMeta.protected voidreadCommaOrEndArray(int c) State-machine dispatch: handles the next character atS04_expectCommaOrEndArray.protected voidreadCommaOrEndObject(int c) State-machine dispatch: handles the next character atS03_expectCommaOrEndObject.protected voidreadFieldNameOrEndObject(int c) State-machine dispatch: handles the next character atS01_expectFieldName.protected voidreadKeywordValue(int c) Reads one of the JSON keyword literals (true ,false ,null ) starting with the given lookahead character.protected voidReads a JSON number from the current input position.protected intReads the next non-whitespace, non-comment character or -1 at end of stream.protected StringReads a double-quoted JSON string from the current input position.protected voidreadValueStart(int c) State-machine dispatch: handles the next character atS00_expectValue.voidSkips over the children of the current structural token.Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, waitMethods inherited from interface org.apache.juneau.marshall.stream.RecordReader
iterator, streamMethods inherited from interface org.apache.juneau.marshall.stream.TokenReader
getExtType, getNativeKind, getSimpleValue, getTag, getTagCount, isStreaming
-
Field Details
-
S00_expectValue
State-machine state: at root, after[ , after: , or after, inside an array. The next read should be a JSON value (object, array, or scalar).- See Also:
-
S01_expectFieldName
State-machine state: after{ or after, inside an object. The next read should be a field name (string).- See Also:
-
S02_expectColon
State-machine state: just emitted aTokenType.FIELD_NAME. The next char should be: .- See Also:
-
S03_expectCommaOrEndObject
State-machine state: just emitted a value inside an object. The next char should be, (followed by another field name) or} .- See Also:
-
S04_expectCommaOrEndArray
State-machine state: just emitted a value inside an array. The next char should be, (followed by another value) or] .- See Also:
-
S05_end
State-machine state: the root document has been fully consumed. The next call tonext()returnsTokenType.END_OF_STREAM.- See Also:
-
r
Underlying character-level reader; subclasses use this for directread/unread/mark/getMarkedcalls during dialect-specific tokenization. -
settings
Cursor-level settings (currently justtrimStrings ). -
currentToken
The token most recently emitted bynext(). Subclasses set this when overriding state-machine dispatch methods. -
state
Current state-machine state; one of theSTATE_*constants. -
depth
Current container nesting depth (0 = root). -
currentFieldName
Field name buffer for the currentTokenType.FIELD_NAMEtoken. -
currentString
String value buffer for the currentTokenType.VALUE_STRINGtoken. -
currentNumberLexeme
Numeric lexeme buffer for the currentTokenType.VALUE_NUMBERtoken (preserved verbatim from the input). -
currentBoolean
Boolean value buffer for the currentTokenType.VALUE_BOOLEANtoken.
-
-
Constructor Details
-
JsonTokenReader
Constructor with defaultJsonTokenReader.Settings.- Parameters:
pipe- The parser input pipe to read from. Must not benull .- Throws:
IOException- If a problem occurred opening the underlying reader.
-
JsonTokenReader
Constructor with explicitJsonTokenReader.Settings.- Parameters:
pipe- The parser input pipe to read from. Must not benull .settings- The cursor-level settings. Must not benull .- Throws:
IOException- If a problem occurred opening the underlying reader.
-
JsonTokenReader
public JsonTokenReader(ParserPipe pipe, JsonTokenReader.Settings settings, JsonParserSession session) throws IOException Constructor used byJsonParser.readTokens(Object)to plumb the calling parser's session in so thatread(Class)can delegate to the existing databind path.- Parameters:
pipe- The parser input pipe to read from. Must not benull .settings- The cursor-level settings. Must not benull .session- TheJsonParserSessionto use forread(Class)delegation, ornull to disablereadon this reader (it will throwUnsupportedOperationException).- Throws:
IOException- If a problem occurred opening the underlying reader.
-
-
Method Details
-
next
Description copied from interface:TokenReaderAdvances the cursor to the next token.- Specified by:
nextin interfaceTokenReader- Returns:
- The token type of the new current token, or
TokenType.END_OF_STREAMwhen the underlying input is exhausted. Never returnsTokenType.NOT_AVAILABLE— that value is reserved for the initial pre-next()state. - Throws:
IOException- If a problem occurred reading from the underlying stream.ParseException- If the input is malformed for the format.
-
getCurrentToken
Description copied from interface:TokenReaderReturns the current token type without advancing.- Specified by:
getCurrentTokenin interfaceTokenReader- Returns:
- The current token type. Returns
TokenType.NOT_AVAILABLEbefore the first call toTokenReader.next(), andTokenType.END_OF_STREAMonce the stream is exhausted.
-
getDepth
Description copied from interface:TokenReaderReturns the current depth of the cursor.Depth starts at
0 at the document root and increases by 1 for eachTokenType.START_OBJECT/TokenType.START_ARRAYencountered (and decreases on the matching end tokens).- Specified by:
getDepthin interfaceTokenReader- Returns:
- The current depth.
-
getFieldName
Description copied from interface:TokenReaderReturns the field name when the current token isTokenType.FIELD_NAME.- Specified by:
getFieldNamein interfaceTokenReader- Returns:
- The field name.
-
getString
Description copied from interface:TokenReaderReturns the current scalar value as a string.Defined for
TokenType.VALUE_STRING(the natural value),TokenType.FIELD_NAME(returns the field name),TokenType.VALUE_NUMBER(returns the lexeme), andTokenType.VALUE_BOOLEAN(returns"true" /"false" ). ForTokenType.VALUE_NULLreturnsnull .- Specified by:
getStringin interfaceTokenReader- Returns:
- The string view of the current value.
-
getNumber
Description copied from interface:TokenReaderReturns the current numeric value as a parsedNumber.Implementations may parse lazily; the returned
Numbersubtype is implementation- and format-defined (e.g.Long/Double/BigDecimal). UseTokenReader.getNumberLexeme()to recover the original textual form.- Specified by:
getNumberin interfaceTokenReader- Returns:
- The parsed numeric value.
- Throws:
ParseException- If the lexeme cannot be parsed.
-
getNumberLexeme
Description copied from interface:TokenReaderReturns the original numeric lexeme for the current numeric token.For text formats this is the exact substring that produced the number. For binary formats the implementation returns a canonical text rendering of the native numeric token.
- Specified by:
getNumberLexemein interfaceTokenReader- Returns:
- The numeric lexeme.
-
getBool
Description copied from interface:TokenReaderReturns the current boolean value.- Specified by:
getBoolin interfaceTokenReader- Returns:
- The boolean value.
-
getBinary
Description copied from interface:TokenReaderReturns the current binary value.Binary scalars come from binary formats only (cbor, msgpack, bson, ...). Text formats deliver embedded binary content as base64-encoded
TokenType.VALUE_STRING.- Specified by:
getBinaryin interfaceTokenReader- Returns:
- The binary value.
-
canRead
Description copied from interface:TokenReaderReturnstrue ifTokenReader.read(Class)is currently callable on this cursor — i.e. the cursor is positioned at a value boundary.This is the natural loop predicate when streaming records:
try (TokenReaderr = Json.DEFAULT .readTokens(input )) {r .next();// consume the opening [ while (r .canRead()) { Itemitem =r .read(Item.class ); process(item ); }r .next();// consume the closing ] }- Specified by:
canReadin interfaceRecordReader- Specified by:
canReadin interfaceTokenReader- Returns:
true if the next operation can beread;false if the cursor is positioned at a structural separator, field name, end-container, or end-of-stream.- Throws:
IOException- If a problem occurred peeking at the underlying stream.ParseException- If the input is malformed for the format.
-
allowsTrailingComma
Subclass extension point: returns whether trailing commas are accepted before] /} .Default returns
false (strict RFC-8259 JSON). Json5TokenReader overrides this to returntrue for the JSON5 dialect.- Returns:
true if a trailing comma should be silently accepted;false if it should produce a parse error.
-
read
Description copied from interface:TokenReaderConsumes the upcoming value (object, array, or scalar) and binds it to a POJO via the format's existing parser.Cursor positioning: the cursor must be in a state where the next call to
TokenReader.next()would emit a value-shaped token (i.e.TokenType.START_OBJECT,TokenType.START_ARRAY, or one of theVALUE_*tokens). Callingreadconsumes that upcoming value wholesale and advances the cursor's depth back to where it was before the value would have started — structurally equivalent to callingTokenReader.next()on a scalar, orTokenReader.next()+TokenReader.skipChildren()on a container.Do NOT call
TokenReader.next()beforeread. Once the structural opener (e.g.{ or[ ) has been consumed byTokenReader.next(), the underlying parser cannot re-bind the value. The contract is "instead-of-next, not after-next."Default behavior: the default implementation throws
UnsupportedOperationException. Format-specific token readers override this by delegating to their existing*ParserSessionat the cursor's current input position and resyncing the cursor's state machine afterward.- Specified by:
readin interfaceRecordReader- Specified by:
readin interfaceTokenReader- Type Parameters:
T- The expected return type.- Parameters:
type- The target Java type.- Returns:
- The bound POJO.
- Throws:
IOException- If a problem occurred reading from the underlying stream.ParseException- If the input is malformed for the format or cannot be bound to the type.
-
read
Description copied from interface:TokenReaderSame asTokenReader.read(Class)but accepts a pre-resolvedClassMeta.- Specified by:
readin interfaceRecordReader- Specified by:
readin interfaceTokenReader- Type Parameters:
T- The expected return type.- Parameters:
type- The target type.- Returns:
- The bound POJO.
- Throws:
IOException- If a problem occurred reading from the underlying stream.ParseException- If the input is malformed or cannot be bound to the type.
-
read
Description copied from interface:TokenReaderSame asTokenReader.read(Class)but accepts a parameterizedTypeand optional type arguments (the same shape asParser.read(Object, Type, Type...)).- Specified by:
readin interfaceRecordReader- Specified by:
readin interfaceTokenReader- Type Parameters:
T- The expected return type.- Parameters:
type- The target type.args- Optional type arguments for parameterized types.- Returns:
- The bound POJO.
- Throws:
IOException- If a problem occurred reading from the underlying stream.ParseException- If the input is malformed or cannot be bound to the type.
-
skipChildren
Description copied from interface:TokenReaderSkips over the children of the current structural token.If the current token is
TokenType.START_OBJECTorTokenType.START_ARRAY, the cursor is advanced to the matchingTokenType.END_OBJECTorTokenType.END_ARRAY(i.e. the entire sub-tree is consumed). For any other token this is a no-op.- Specified by:
skipChildrenin interfaceTokenReader- Throws:
IOException- If a problem occurred reading from the underlying stream.ParseException- If the input is malformed for the format.
-
close
Description copied from interface:TokenReaderCloses the underlying stream.Implementations should be idempotent — closing a closed reader is a no-op.
- Specified by:
closein interfaceAutoCloseable- Specified by:
closein interfaceCloseable- Specified by:
closein interfaceRecordReader- Specified by:
closein interfaceTokenReader- Throws:
IOException
-
readValueStart
State-machine dispatch: handles the next character atS00_expectValue.Subclasses (e.g. Json5TokenReader) override this to add dialect-specific value shapes (single-quoted strings, bare identifiers, missing-value handling).
- Parameters:
c- The next non-whitespace, non-comment character read from the input.- Throws:
IOException- If a problem occurred reading from the underlying stream.ParseException- If the input is malformed at this position.
-
readFieldNameOrEndObject
State-machine dispatch: handles the next character atS01_expectFieldName.Subclasses (e.g. Json5TokenReader) override this to accept bare-identifier field names and single-quoted field names.
- Parameters:
c- The next non-whitespace character read from the input.- Throws:
IOException- If a problem occurred reading from the underlying stream.ParseException- If the input is malformed at this position.
-
readCommaOrEndObject
State-machine dispatch: handles the next character atS03_expectCommaOrEndObject.Subclasses (e.g. Json5TokenReader) override this to accept trailing commas before
} .- Parameters:
c- The next non-whitespace character read from the input.- Throws:
IOException- If a problem occurred reading from the underlying stream.ParseException- If the input is malformed at this position.
-
readCommaOrEndArray
State-machine dispatch: handles the next character atS04_expectCommaOrEndArray.Subclasses (e.g. Json5TokenReader) override this to accept trailing commas before
] .- Parameters:
c- The next non-whitespace character read from the input.- Throws:
IOException- If a problem occurred reading from the underlying stream.ParseException- If the input is malformed at this position.
-
afterValue
Sets the post-value state based on the enclosing container.Subclasses (e.g. JsonlTokenReader) override this when the format treats consecutive top-level values differently — JSONL transitions back to
S00_expectValueat depth 0 instead ofS05_endso the next line is read. -
readString
Reads a double-quoted JSON string from the current input position.Handles escape sequences (
\\n ,\\t ,\\uNNNN , etc.) inline viaParserReader.replace(char). Subclasses (e.g. Json5TokenReader) override this to also accept single-quoted strings.- Returns:
- The parsed string (without surrounding quotes).
- Throws:
IOException- If a problem occurred reading from the underlying stream.ParseException- If the input is malformed at this position.
-
readKeywordValue
Reads one of the JSON keyword literals (true ,false ,null ) starting with the given lookahead character. SetscurrentTokenandcurrentBooleanaccordingly.- Parameters:
c- The first character of the keyword (one of't' ,'f' ,'n' ).- Throws:
IOException- If a problem occurred reading from the underlying stream.ParseException- If the keyword is malformed.
-
readNumberValue
Reads a JSON number from the current input position. Captures the lexeme viaParserReader.readNumberString()and validates it; defers actual numeric parsing togetNumber()(lazy). SetscurrentNumberLexemeandcurrentToken.- Throws:
IOException- If a problem occurred reading from the underlying stream.ParseException- If the input is not a valid JSON number.
-
readSkipWsAndComments
Reads the next non-whitespace, non-comment character or -1 at end of stream.- Returns:
- The next significant character, or
-1 at end of input. - Throws:
IOException- If a problem occurred reading from the underlying stream.ParseException- If a JSON comment is malformed.
-
isNumberStart
Returns whether the given character could start a JSON number lexeme (digit or minus sign).- Parameters:
c- The character.- Returns:
true if the character could start a number.
-
isDigit
Returns whether the given character is an ASCII decimal digit.- Parameters:
c- The character.- Returns:
true if the character is between'0' and'9' inclusive.
-
pushContainer
Pushes a new container onto the cursor's depth stack.- Parameters:
isArray-true if the new container is an array;false for an object.
-
popContainer
Pops the top container off the cursor's depth stack.- Throws:
ParseException- If the stack is already empty (defensive only; well-formed JSON cannot trigger this).
-
isInArray
Returns whether the cursor's current container (the one most recently pushed) is an array.- Returns:
true if currently inside an array;false if inside an object.
-
parseException
Builds aParseExceptionwith the given message +String.format-style (%s ) arguments. Used by all dispatch methods to surface input-malformed errors with consistent formatting.- Parameters:
message- The exception message format string (%s ,%d , ...).args- The format arguments.- Returns:
- A new
ParseExceptionready to throw.
-