Kite

json

std/json — reading and writing JSON.

Written in Kite, on top of the five string primitives the language has and text.from_code. It is the first substantial body of Kite code in the standard library, and a parse returns (Json, error) like anything else that can fail: a malformed document is a value to handle, not something thrown.

Escapes are RFC 8259 §7, both ways. \uXXXX is decoded, including a surrogate pair as the one character it denotes, and every control character is escaped on the way out. Neither used to be true: caf\u00e9 decoded to cafu00e9 — silently, so a name simply came out wrong — and a carriage return went out raw, which is not JSON and which JSON.parse refuses with Bad control character in string literal. The decoding is text.from_code, which std/toml uses for the same purpose.

What is read is RFC 8259, and nothing looser. A leading zero (01), a point or an exponent with no digits after it (1., 1e), an escape the RFC does not list (\x) and a raw control character inside a string are each refused with an error saying where, rather than read as whatever they looked closest to. A document this accepts is one every other parser accepts too. Numbers are read by the prelude's parse_float, so each is the float nearest what the document wrote — 0.3 is 0.3 — and one too large for a float is an error.

Json

pub enum Json

A JSON value.

Recursive with no boxing annotation, because every Kite aggregate is already a GC reference.

parse

pub fn parse(input: str) -> (Json, error)

The value a document holds, or an error saying where it went wrong.

stringify

pub fn stringify(value: Json) -> str

A value as compact JSON.

An infinity or a NaN is written null, as JSON.stringify writes it: JSON has no spelling for either, so the choice is between that and a document nothing can read back.

pretty

pub fn pretty(value: Json) -> str

A value as indented JSON, for something a person will read.

field

pub fn field(value: Option<Json>, key: str) -> Option<Json>

A field of an object.

Every accessor takes an optional value and yields one, which is what makes them chain: json.field(json.field(doc, "a"), "b") reads left to right and yields nil the moment anything is missing. A plain Json is accepted wherever an Option<Json> is wanted, so the first call in a chain needs nothing special.

Kite has no ?., and this is why it does not need one: an operation that might find nothing says so in its result type, and the branch is taken once at the end rather than hidden at every step.

at

pub fn at(value: Option<Json>, index: int) -> Option<Json>

text

pub fn text(value: Option<Json>) -> Option<str>

number_of

pub fn number_of(value: Option<Json>) -> Option<float>

int_of

pub fn int_of(value: Option<Json>) -> Option<int>

A whole number, or nil — for anything that is not a number, and for a number that is not whole or does not fit in an int.

The cast this used to be truncated 3.7 to 3 and saturated 1e30 to the largest int, and @derive(Decode) reads an int field through this, so a decoder that promises "expected a whole number" accepted both. JSON's one number type is a float, so 3.0 is the whole number 3.

bool_of

pub fn bool_of(value: Option<Json>) -> Option<bool>

items

pub fn items(value: Option<Json>) -> [Json]

The elements of an array, or nothing at all — a caller iterating them wants a slice rather than a branch.

entries

pub fn entries(value: Option<Json>) -> { str: Json }

An object's entries, in the order the document wrote them.

The counterpart to items for the other container. Anything that is not an object yields nothing rather than an error, which is what makes the accessors chain: a caller that needs to tell "not an object" from "an empty object" matches on the Json itself, which is still there.

is_null

pub fn is_null(value: Option<Json>) -> bool

Whether a document says nothing here.

A field that is absent and a field written null both answer true, and that is the point: the two mean the same thing to every producer of JSON, and a reader that told them apart would be reading a distinction the format does not reliably carry. @derive(Decode) uses this for an Option<T> field.

Encode

pub trait Encode

A value that can become JSON.

Declared here rather than in the prelude because it mentions Json, and a trait cannot be declared somewhere that has never heard of the type it returns. @derive(Encode) writes the body; the decoding direction is an associated function on the type — User.decode(doc) — because a trait method cannot say it returns the implementing type.

text_or

pub fn text_or(value: Option<Json>, key: str, fallback: str) -> str

A field's text, or a fallback — the shape most configuration reading takes.

int_or

pub fn int_or(value: Option<Json>, key: str, fallback: int) -> int

bool_or

pub fn bool_or(value: Option<Json>, key: str, fallback: bool) -> bool