ddn.data.json

High-Performance Strict JSON Parser/Writer

This module provides a strictly RFC 8259 compliant JSON parser and writer, optimized for performance. Unlike ddn.data.json5, this module does not support JSON5 extensions such as comments, trailing commas, unquoted keys, single-quoted strings, hex numbers, or special numbers (NaN/Infinity).

Mapping rules to var:

  • Objects: map to var.Type.OBJECT with string keys and var values.
  • Arrays: map to var.Type.ARRAY with elements converted to var recursively.
  • Strings: map to var.Type.STRING (double-quoted only).
  • Booleans: map to var.Type.BOOL.
  • null: map to var.Type.NULL.
  • Numbers: integers use Type.LONG or Type.ULONG; decimals use Type.DOUBLE.

Writer rules:

  • Keys are always double-quoted per RFC 8259.
  • Values are emitted compactly by default; pretty printing is configurable.
  • Deterministic output is achieved by lexicographically sorting object keys by default.

Examples

import ddn.data.json : parseJSON, toJSON, JsonError;
import ddn.var : var;

JsonError err;
var v;
assert(parseJSON(v, `{"a":1, "b":"x"}`, err));
assert(v["a"].as!long == 1);

// Serialize back to JSON
auto jsonText = toJSON(v);  // e.g. `{"a":1,"b":"x"}`

See Also

ddn.data.json5 for JSON5 support with extensions.

Types 15

Policy for handling duplicate keys in JSON objects.

JSON objects with duplicate keys are technically valid per RFC 8259, but their behavior is implementation-defined. This enum controls how the parser handles such cases.

LAST_WINSLast occurrence of a duplicate key wins (default, matches most browsers).
FIRST_WINSFirst occurrence of a duplicate key wins.
ERRORDuplicate keys cause a parsing error.

Policy controlling parser and writer behavior.

This struct provides configuration options that affect how JSON is parsed and serialized, allowing customization for different use cases.

Fields
bool preferSignedIntegersPrefer signed integers when a value fits both signed and unsigned.
size_t maxDepthMaximum nesting depth allowed during parsing to prevent runaway recursion.
JsonDuplicateKeyPolicy duplicateKeyPolicyHow to handle duplicate keys in objects.
bool sortKeysOnWriteWriter: sort object keys for deterministic output.
bool asciiOnlyWriteWriter: emit only ASCII by escaping non-ASCII code points.
bool prettyPrintWriter: enable multi-line pretty printing with indentation.
string indentWriter: indentation string for pretty printing (used only when `prettyPrint`).

Options controlling JSON serialization behavior.

This struct provides fine-grained control over how values are serialized to JSON text.

Fields
bool prettyPretty-print output with indentation.
string indentIndentation string for pretty printing (used only when `pretty`).
bool asciiOnlyEscape non-ASCII code points as \uXXXX.
bool sortKeysSort object keys for deterministic output.
size_t maxDepthMaximum nesting depth allowed during writing to prevent runaway recursion. When exceeded, the writer emits `null` at the offending position.

Reusable scratch buffers for JSON writing.

This structure allows caller-controlled reuse of temporary allocations across repeated serializations, reducing GC pressure for high-throughput scenarios.

Currently used for:

  • Deterministic object serialization (sortKeys == true) via per-depth key buffers.
Fields
string[][] keyBuffersPer-depth buffers used to collect and sort object keys.
enumJsonTokenKind : ubyte

Token kinds produced by the JSON lexer.

These represent the fundamental syntactic elements of JSON text as defined by RFC 8259.

ENDEnd of input (successful completion).
ERRORLexical error (invalid token).
LBRACELeft brace `{` — start of object.
RBRACERight brace `}` — end of object.
LBRACKETLeft bracket `[` — start of array.
RBRACKETRight bracket `]` — end of array.
COLONColon `:` — key-value separator.
COMMAComma `,` — element separator.
STRINGString literal (double-quoted).
NUMBERNumber literal.
TRUE_LITKeyword `true`.
FALSE_LITKeyword `false`.
NULL_LITKeyword `null`.
structJsonToken

A token produced by the JSON lexer.

Tokens represent individual syntactic elements extracted from the input. The lexeme field is a slice into the original source, avoiding allocation during tokenization.

Fields
JsonTokenKind kindThe kind of token.
const(char)[] lexemeSlice into the source text containing the token's characters.
size_t line1-based line number where the token starts.
size_t column1-based column number where the token starts.
size_t startIndex0-based byte offset where the token starts.
size_t endIndex0-based byte offset where the token ends (exclusive).
structJsonLexer

High-performance JSON lexer over a UTF-8 buffer.

The lexer tokenizes strict JSON according to RFC 8259:

  • Skips ASCII whitespace only (space, tab, newline, carriage return).
  • No BOM handling — strict JSON does not allow BOM.
  • No comment handling — JSON does not support comments.
  • Returns slices for strings and numbers without allocating.
  • Tracks line/column for diagnostics.

Performance optimizations:

  • Compile-time lookup tables for character classification.
  • Single-pass tokenization without backtracking.
  • Zero allocations during tokenization (returns slices).
Fields
private const(char)[] _src
private size_t _i
private size_t _line
private size_t _col
Methods
size_t line() @property const @safeCurrent 1-based line number in the input.
size_t column() @property const @safeCurrent 1-based column number in the input.
JsonToken nextToken() @safeReturn the next token from the input stream.
private JsonToken _tok(JsonTokenKind k, size_t start, size_t len) @safe
private void _advanceOne() @safe
private void _skipWhitespace() @safe
private JsonToken _scanString() @safe
private JsonToken _scanKeyword(string keyword, JsonTokenKind kind) @safe
private JsonToken _scanNumber() @safe
private JsonToken _errorToken(size_t start) @safe
Constructors
this(const(char)[] input)Construct a lexer over the given input.
aliasJsonReviver = var delegate(const string key, var value) @safe

Reviver delegate type for transforming parsed values.

The callback is invoked in post-order (children first), similarly to JavaScript JSON.parse(text, reviver).

Parameters

keyObject key or array index (as decimal string). Root is passed as `""`.
valueCurrent value.

Returns

The replacement value.
private structJsonParser

Internal parser state for JSON parsing.

Fields
private JsonLexer _lexer
private const(char)[] _src
private JsonPolicy _policy
private JsonToken _current
private size_t _depth
Methods
bool parse(out var value, out JsonError err) @safe
private void _advance() @safe
private bool _parseValue(out var value, out JsonError err) @safe
private bool _parseObject(out var value, out JsonError err) @safe
private bool _parseArray(out var value, out JsonError err) @safe
private bool _parseString(out var value, out JsonError err) @safe
private bool _parseNumber(out var value, out JsonError err) @safe
Constructors
this(const(char)[] input, JsonPolicy policy)

Callbacks for SAX-style JSON parsing.

Any callback may be left null to ignore that event.

Event semantics:

  • onObjectStart / onObjectEnd are emitted for `{` / `}`.
  • onArrayStart / onArrayEnd are emitted for `[` / `]`.
  • onKey is emitted for each object property key, before its value.
  • onValue is emitted for scalar values only (null/bool/number/string).
Fields
void delegate() @safe onObjectStartCalled when an object begins (`{`).
void delegate() @safe onObjectEndCalled when an object ends (`}`).
void delegate() @safe onArrayStartCalled when an array begins (`[`).
void delegate() @safe onArrayEndCalled when an array ends (`]`).
void delegate(const string key) @safe onKeyCalled for each object key.
void delegate(const var value) @safe onValueCalled for each scalar value (null, bool, number, string).
enumJsonStreamResult : ubyte

Result of a streaming JSON parse attempt.

NEED_MOREThe parser needs more input to decide.
OKA complete JSON value was parsed successfully.
ERRORA syntax or lexical error occurred.

Incremental/streaming JSON parser that accepts input in chunks.

This API is intended for large inputs or network streams where the complete document may not be available at once.

Notes:

  • The parser produces at most one root JSON value.
  • After a successful parse (JsonStreamResult.OK), the instance becomes done;

    call reset() to parse another document.

  • When finish() has not been called yet, parse failures that look like

    "unexpected end of input" are reported as NEED_MORE.

Examples

import ddn.data.json : JsonStreamParser, JsonStreamResult, JsonError;
import ddn.var : var;

JsonStreamParser sp;
sp.push(`{"a":1,`);
var v; JsonError err;
assert(sp.tryParse(v, err) == JsonStreamResult.NEED_MORE);
sp.push(`"b":2}`);
assert(sp.tryParse(v, err) == JsonStreamResult.OK);
assert(v["b"].as!long == 2);
Fields
private string _buffer
private JsonPolicy _policy
private bool _finished
private bool _done
Methods
void push(const(char)[] chunk) @safeAppend a new chunk of input to the internal buffer.
void finish() @safeMark the stream as finished.
void reset() @safeReset the parser state and clear all buffered input.
bool done() @property const @safeTrue once a full root value has been successfully parsed.
size_t bufferedLength() @property const @safeCurrent buffered input size in bytes.
JsonStreamResult tryParse(out var value, out JsonError err) @safeAttempt to parse the buffered data as a single JSON root value.
private bool _looksIncomplete(const ref JsonError e) const @safeHeuristic: detect failures likely caused by an incomplete final chunk.
Constructors
this(const JsonPolicy policy)Construct a streaming parser using `policy`.
aliasJsonReplacer = var delegate(const string key, var value) @safe

Replacer delegate type for transforming/filtering values during serialization.

The callback is invoked for each value during JSON serialization, allowing transformation or filtering of values before they are written.

Parameters

keyObject key or array index (as decimal string). Root is passed as `""`.
valueCurrent value to be serialized.

Returns

The replacement value. Return var.init (null) to omit the key from output

(only effective for object properties, not array elements).

enumJsonErrorCode : ushort

Error codes for JSON parsing and writing operations.

These codes categorize the types of errors that can occur during JSON processing, enabling programmatic error handling.

UNKNOWNUnknown or unspecified error.
TRAILING_CONTENTTrailing non-whitespace content after a complete root value.
UNEXPECTED_TOKENUnexpected token kind for the current parse state.
UNEXPECTED_EOFUnexpected end of input.
INVALID_TOKENTokenization failure (invalid UTF-8, malformed token, etc.).
INVALID_NUMBERInvalid number literal syntax.
INVALID_STRINGInvalid string literal syntax.
INVALID_ESCAPEInvalid escape sequence in a string literal.
MAX_DEPTH_EXCEEDEDParser recursion limit exceeded.
DUPLICATE_KEYDuplicate key in object (optional strict mode).
INVALID_ARGUMENTInvalid argument passed to a helper API.
IO_ERRORI/O error while reading/writing.
structJsonError

A non-throwing error report produced by the JSON parser/writer.

This struct captures detailed information about parsing or writing errors, including position information for diagnostics.

Fields
size_t line1-based line number where the error occurred (0 if unknown).
size_t column1-based column number where the error occurred (0 if unknown).
size_t index0-based byte offset into the original input (0 if unknown).
JsonErrorCode codeMachine-readable error category.
string messageHuman-readable message.
string contextExcerpt of the source around `index` with a caret line, if available.
Constructors
this(size_t line, size_t column, string message, JsonErrorCode code = JsonErrorCode.UNKNOWN, size_t index = 0, string context = "")Construct an error report.

Functions 33

private fnstring[] _scratchKeys(ref JsonWriteScratch scratch, const size_t depth) ref @safeObtain a cleared scratch key buffer for a given writer recursion `depth`.
fnbool decodeJsonString(const(char)[] lexeme, out string decoded, out string errorMsg) @safeDecode a JSON string literal, processing escape sequences.
private fnbool _parseDouble(const(char)[] s, out double result) @trustedParse a string as a double value.
private fnbool _parseDoubleStdConv(const(char)[] s, out double result) @safeStandard library fallback for parseDouble.
fnbool parseJsonNumber(const(char)[] lexeme, out var result, out string errorMsg, bool preferSigned = true) @safeParse a JSON number literal and return the appropriate `var` type.
fnbool parseJSON(out var value, const(char)[] input, out JsonError err, JsonPolicy policy = JsonPolicy.init) @safeParse JSON text into a `var` value.
fnvar parseJSON(const(char)[] input) @safeConvenience overload that parses JSON and returns the value directly.
fnbool parseJSON(out var value, const(char)[] input, JsonReviver reviver, out JsonError err, JsonPolicy policy = JsonPolicy.init) @safeParse JSON text and apply `reviver` to each value.
private fnvoid _applyReviver(ref var value, const string key, scope JsonReviver reviver) @safeApply `reviver` to `value` in post-order traversal.
fnbool parseJsonSax(const(char)[] input, ref JsonSaxHandler handler, out JsonError err, const JsonPolicy policy = JsonPolicy.init) @safeParse JSON text and emit SAX-style events into `handler`.
fnbool isValidJSON(const(char)[] input, out JsonError err, JsonPolicy policy = JsonPolicy.init) @safeValidate JSON text without building a value tree.
fnbool isValidJSON(const(char)[] input) @safeConvenience overload that validates JSON without returning error details.
private fnbool _validateValue(const(char)[] src, ref JsonLexer lx, ref JsonToken cur, out JsonError err, const JsonPolicy policy, size_t depth) @safeValidate a JSON value without building a var tree.
private fnbool _validateArray(const(char)[] src, ref JsonLexer lx, ref JsonToken cur, out JsonError err, const JsonPolicy policy, size_t depth) @safeValidate a JSON array structure.
private fnbool _validateObject(const(char)[] src, ref JsonLexer lx, ref JsonToken cur, out JsonError err, const JsonPolicy policy, size_t depth) @safeValidate a JSON object structure.
fnbool minify(out string output, const(char)[] input, out JsonError err) @safeMinify JSON text by removing all optional whitespace.
fnstring minify(const(char)[] input) @safeConvenience overload that minifies JSON and returns the result directly.
fnstring toJSON(const var value, JsonWriteOptions opts = JsonWriteOptions.init) @safeSerialize a `var` value to a JSON string.
fnstring toJSON(const var value, JsonWriteOptions opts, ref JsonWriteScratch scratch) @safeSerialize a `var` value to JSON using the provided scratch buffers.
fnvoid writeJSON(W)(ref W output, const var value, JsonWriteOptions opts = JsonWriteOptions.init) @safeWrite a `var` value as JSON to an output range.
fnstring toJSON(const var value, JsonReplacer replacer, JsonWriteOptions opts = JsonWriteOptions.init) @safeSerialize a `var` value to JSON with a replacer function.
private fnvar _applyReplacer(const var value, const string key, scope JsonReplacer replacer) @safeApply replacer to a value recursively.
private fnvoid _writeJsonImpl(W)(const var value, ref W buf, const JsonWriteOptions opts, uint depth, ref JsonWriteScratch scratch) @safeInternal implementation of JSON writing.
private fnvoid _writeDouble(W)(double d, ref W buf) @safeWrite a double value in RFC 8259 compliant format.
private fnvoid _writeString(W)(scope const(char)[] s, ref W buf, bool asciiOnly) @safeWrite a string value with proper escaping.
private fnvoid _writeArray(W)(const var value, ref W buf, const JsonWriteOptions opts, uint depth, ref JsonWriteScratch scratch) @safeWrite an array value.
private fnvoid _writeObject(W)(const var value, ref W buf, const JsonWriteOptions opts, uint depth, ref JsonWriteScratch scratch) @safeWrite an object value.
private fnvoid _writeIndent(W)(ref W buf, string indent, uint depth) @safeWrite indentation for pretty printing.
private fnbool _isUtf8Continuation(char b) @safe pure nothrow @nogcChecks if a byte is a UTF-8 continuation byte (10xxxxxx pattern).
private fnsize_t _alignUtf8Start(const(char)[] src, size_t pos, size_t minPos) @safe pure nothrow @nogcAdjusts a position backwards to align with a UTF-8 character boundary.
private fnsize_t _alignUtf8End(const(char)[] src, size_t pos, size_t maxPos) @safe pure nothrow @nogcAdjusts a position forwards to align with the end of a UTF-8 character.
private fnstring _jsonContextWindow(const(char)[] src, size_t index, size_t context = 30) @safeBuild a context window for diagnostics.
private fnJsonError _makeError(const(char)[] src, const size_t line, const size_t column, const size_t index, const JsonErrorCode code, const string msg) @safeBuild a structured error with a context window.

Variables 5

private varbool[256] _jsonWhitespace

Lookup table for JSON whitespace characters.

RFC 8259 defines whitespace as: space (0x20), tab (0x09), newline (0x0A), carriage return (0x0D). This table allows O(1) whitespace detection instead of multiple comparisons.

private varbool[256] _jsonDigit

Lookup table for decimal digits (0-9).

private varbool[256] _jsonHexDigit

Lookup table for hexadecimal digits (0-9, a-f, A-F).

private varubyte[256] _jsonHexValue

Lookup table for hex digit values (0-15, or 0xFF for invalid).

private varchar[256] _jsonEscapeChar

Lookup table for valid single-character escape sequences.

Maps escape character to decoded value, or 0 for invalid. Valid escapes: `"`, `\`, `/`, b, f, n, r, t