Skip to content
CryoCryo home
StdlibText and data

json

A complete RFC 8259 value model, parser, and serializer. Not auto-imported.

import std::json;

match (parse(source)) {
    Result::Ok(doc) => {
        match (doc.get(Str::new("port"))) {
            Option::Some(v) => { /* v: JsonValue* */ }
            Option::None    => { /* absent */ }
        }
    }
    Result::Err(e) => {
        eprintln(f"{e.line}:{e.column}: {e.describe()}");
    }
}
ItemImport
JsonValue, JsonNumber, JsonObjectimport std::json::value;
parse, MAX_DEPTHimport std::json::parser;
stringify, stringify_pretty, write_valueimport std::json::serializer;
JsonError, JsonErrorKindimport std::json::error;

JsonValue

A tagged union over the six JSON types. It is recursive through Array<JsonValue> and JsonObject, each owning variant holds heap storage, and drop frees the tree recursively.

type enum JsonValue {
    Null;
    Bool(boolean);
    Number(JsonNumber);
    String(String);
    Array(Array<JsonValue>);
    Object(JsonObject);
}

Constructing

ConstructorProduces
static null_value()Null
static bool_value(b: boolean)Bool
static int_value(n: i64)Number(Int)
static uint_value(n: u64)Number(UInt)
static float_value(n: f64)Number(Float)
static string_value(s: String)String, taking ownership
static from<T>(src: T)Compile-time dispatch over the scalar types
static string_array(items: &string[])Array of strings
static empty_array() / empty_object()

Inspecting

is_null, is_bool, is_number, is_string, is_array, and is_object each return boolean.

MethodReturnsNotes
get(key: Str)Option<JsonValue*>Object member lookup, O(1) average.
at(index: u64)Option<JsonValue*>Array element.
length()u64Elements of an array, members of an object.
as<T>()Option<T>Typed extraction, where T: TryFrom<&JsonValue>.

TryFrom<&JsonValue> is implemented for boolean, i64, u64, f64, and Str, so as<T>() covers the usual extractions:

const port: Option<i64> = doc.get(Str::new("port")).unwrap_ptr().as<i64>();

JsonNumber

JSON has one number type; this splits it by source representation so integers round-trip exactly — 42 parses and re-serializes as 42, not 42.0.

type enum JsonNumber { Int(i64); UInt(u64); Float(f64); }

Int covers signed values fitting i64, UInt carries values in (i64::MAX, u64::MAX], and everything else — decimals, exponents — is Float. Lossy conversion is opt-in: as_i64() returns None for a UInt beyond i64::MAX or a Float with a fractional component, and as_f64() gives the lossy view when you want it.

JsonObject

Object access is O(1) on average through a hash index, and insertion order is preserved in a parallel keys array, so serialization is deterministic.

MethodNotes
static new() / with_capacity(capacity: u64)
length() / is_empty()
insert(key: String, value: JsonValue)Takes ownership of both.
put(key: string, value: JsonValue)Convenience over a C string literal.
get(key: Str)Option<JsonValue*>
contains(key: Str)boolean
key_at(index: u64) / value_at(index: u64)Insertion-ordered access.

Parsing

parse(text: Str) -> Result<JsonValue, JsonError> parses a complete document. Trailing whitespace is fine; trailing anything else is TrailingData.

The parser is single-pass recursive descent over a borrowed Str, and it is RFC 8259 strict: no trailing commas, no unquoted keys, no comments. String literals decode \X and \uXXXX escapes, and UTF-16 surrogate pairs are recombined; a lone surrogate is InvalidUnicode.

MAX_DEPTH caps combined object and array nesting at 256. The default is generous for hand-authored data and tight enough that a malicious megabyte of [[[...]]] cannot exhaust the stack.

Serializing

FunctionNotes
stringify(v: &JsonValue) -> StringCompact — no whitespace between tokens.
stringify_pretty(v: &JsonValue) -> StringTwo-space indent, one entry per line.
write_value(out: mut &String, v: &JsonValue)Append compact output to a string you own.
write_pretty(out: mut &String, v: &JsonValue)Append pretty output.

The write_* forms exist so several values can be concatenated into one buffer without intermediate allocations. Empty objects and arrays stay on a single line even in pretty mode.

String output escapes per the spec: UTF-8 bytes outside the ASCII control range pass through, and bytes below 0x20 are emitted as \uXXXX.

The serializer mirrors the parser's depth cap, so a parsed tree always round-trips; only a programmatically built tree deeper than 256 is bounded, and that is there to stop unbounded recursion on adversarial input.

JsonError

Modelled as a kind plus a position plus a message. Branch on kind for programmatic decisions; msg carries the human-readable detail.

Field / methodNotes
kindJsonErrorKind — the closed set of parser failures.
line / column1-indexed, matching editor conventions.
offset0-indexed byte offset into the source.
msgOwned String with the detail.
describe()Str — the standard accessor every stdlib error exposes.

JsonErrorKind::label() gives a short diagnostic slug: "unexpected_eof", "invalid_number", and so on.

JsonError owns its message, so it carries a drop.