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 needed text.from_code, which was added for this and for std/toml; only std/toml picked it up.

Json

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.

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>

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

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