reference

Everything public

One section per package, in the order the guide reads them. Each item is the declaration the compiler sees and the sentence the source wrote about it.

package

moonjson

The tree every dialect reads into, the scanner they share, and the writer that puts it back as strict JSON.

commented

pub let commented : Flavor

JSON with comments and trailing commas — what editors and their configuration files have settled on, tsconfig.json being the best-known.

dump

pub fn dump(Json, indent? : Int32, ascii? : Bool, sort? : Bool) -> Bytes

The same, as UTF-8 bytes — the form a body goes onto a wire in.

dumps

pub fn dumps(Json, indent? : Int32, ascii? : Bool, sort? : Bool) -> String

Write a tree as strict JSON, whatever dialect it was read from.

indent of zero, the default, writes the compact form with no space anywhere; anything larger writes one line per member with that many spaces a level, and a space after each colon.

A number that came in as text goes back out as that text, so a document read and written again is unchanged. Only a number with no such text is formatted, and a number that is not finite — which JSON5 can produce and JSON cannot write — becomes null, as every JavaScript implementation does.

load

pub fn load(BytesView, Flavor, bom? : Bool) -> Json raise Refused

Read one document from bytes, which is how one arrives over a wire. A leading byte-order mark is skipped by default. RFC 8259 §8.1 forbids writing one and says nothing about refusing to read one, and refusing would mean rejecting half the files Windows tooling produces.

loads

pub fn loads(StringView, Flavor) -> Json raise Refused

Read one document in the given dialect.

The whole input must be one value: anything after it, other than whitespace and — where the dialect allows them — comments, is Trailing, not silently ignored. A parser that stops at the first value turns a truncated file into a plausible one.

relaxed

pub let relaxed : Flavor

JSON5: everything ECMAScript 5 would have accepted.

strict

pub let strict : Flavor

RFC 8259 and nothing more.

At

pub(all) struct At

Where something went wrong in a document that arrived as text.

The offset is in UTF-16 code units from the start, which is what a StringView indexes by; line and column are one-based, because that is how every editor counts and an error a person cannot find in their file is half an error.

Fields
  • at : Int32

    The offset in UTF-16 code units from the start of the document, which is what a StringView indexes by.

  • line : Int32

    The line the offset is on, counting from one.

  • column : Int32

    The column the offset is at, counting from one.

Methods
  • pub fn At::equal(Self, Self) -> Bool

    Written by derive(Eq).

  • pub fn At::not_equal(Self, Self) -> Bool

    Written by derive(Eq).

  • pub fn At::to_repr(Self) -> @debug.Repr

    Written by derive(Debug).

Duplicates

pub(all) enum Duplicates

What a repeated member name means.

A reader that fills a map keeps the last one, because that is what a map does, so that is the default. The other two are here because a document with two "id" members is usually a mistake, and a reader that wants to hear about it should not have to write its own parser.

Variants
  • Last

    The last one wins. What everybody does.

  • First

    The first one wins.

  • Reject

    A repeated name is an error.

Methods
  • pub fn Duplicates::equal(Self, Self) -> Bool

    Written by derive(Eq).

  • pub fn Duplicates::not_equal(Self, Self) -> Bool

    Written by derive(Eq).

  • pub fn Duplicates::to_repr(Self) -> @debug.Repr

    Written by derive(Debug).

Flavor

pub(all) struct Flavor

How to read a document: what syntax to accept, and what to do at the edges.

The dialects in this family differ only in their syntax — the tree they produce is the same — so the scanner is written once and each dialect is a set of switches. A dialect outside this repository can be described the same way without forking anything.

The last three fields are not syntax but policy. They are here rather than as arguments because they are set once for a deployment and because this family has sixteen entry points: a policy added here costs one line, and a policy added to every signature costs sixteen.

Fields
  • comments : Bool

    // to end of line, and /* … */.

  • trailing_commas : Bool

    A comma before the closing brace or bracket.

  • unquoted_keys : Bool

    A member name written as an identifier rather than a string.

  • single_quotes : Bool

    'like this' as well as "like this".

  • extra_numbers : Bool

    +1, .5, 5., 0xFF, Infinity, -Infinity, NaN.

  • extra_escapes : Bool

    \x41, \0, \v, a backslash before a newline, and a backslash before anything else standing for that character.

  • extra_space : Bool

    The whitespace ECMAScript recognises, not only the four RFC 8259 allows.

  • depth : Int32

    How deep the nesting may go before the document is refused.

    Five hundred is past anything written for a reader and far short of what exhausts a stack on any backend. Readers disagree on the number — a thousand in one, a hundred and twenty-eight in another — but not on having one, because a document of a hundred thousand open brackets is a denial of service rather than a document.

  • surrogates : Bool

    Whether a lone half of a surrogate pair is a character.

    The case is implementation-defined: a reader may keep the half, replace it with U+FFFD, or refuse the document. This one refuses, because a lone half cannot be written back out and every value that could replace it is a guess about what the writer meant.

  • duplicates : Duplicates

    What a repeated member name means.

Methods
  • pub fn Flavor::equal(Self, Self) -> Bool

    Written by derive(Eq).

  • pub fn Flavor::new(comments? : Bool, trailing_commas? : Bool, unquoted_keys? : Bool, single_quotes? : Bool, extra_numbers? : Bool, extra_escapes? : Bool, extra_space? : Bool, depth? : Int32, surrogates? : Bool, duplicates? : Duplicates) -> Self

    Build a dialect by naming the parts that differ from strict JSON.

    The same thing can be written { ..@moonjson.strict, depth: 100 }; this form exists because a named argument reads better when several parts differ.

  • pub fn Flavor::not_equal(Self, Self) -> Bool

    Written by derive(Eq).

  • pub fn Flavor::to_repr(Self) -> @debug.Repr

    Written by derive(Debug).

Refused

pub(all) suberror Refused

Why a document was refused, and where.

One error type for the whole family — reading text, reading bytes, walking a pointer, applying a patch, writing a value a format cannot carry — so that a caller has one thing to catch and one place to look. A variant that has a position carries the At it was found at; a variant about a pointer or a patch names the pointer itself, because a move and a copy each carry two and an error that did not say which one would be half an error.

Variants
  • Unexpected(At, Char)

    A character that cannot start or continue what is being read.

  • Truncated(At)

    The document ends inside a value.

  • BadEscape(At)

    An escape sequence that is not one, or a lone half of a surrogate pair.

  • BadNumber(At)

    A number the grammar does not admit — a leading zero, a bare minus, an exponent with no digits.

  • Trailing(At)

    A second value after the first one ended.

  • TooDeep(At)

    Nesting past the depth limit. Without this, a document of ten thousand open brackets would take the stack down with it.

  • BadUtf8(At)

    Bytes that are not UTF-8, when reading a document that arrived as bytes.

  • Repeated(At, String)

    A member name that appears twice, when the dialect refuses that.

  • Short(at~ : Int32)

    The document ended inside a value.

  • Extra(at~ : Int32)

    Something after the value ended. A document is one value, and a reader that stops at the first turns a corrupt file into a plausible one.

  • Bad(at~ : Int32, why~ : String)

    A byte that cannot be read this way: a reserved additional-information value, a length or an index that does not fit, a string that is not UTF-8, a nesting that went too deep.

  • Unwritable(why~ : String)

    A value the format has no way to write: a number that is not finite into JSON, a byte string into a text dialect, a key that is not a string into one whose objects have only string keys.

  • Syntax(path~ : String, at~ : Int32)

    The pointer is not one: it does not start with a slash, or a ~ is followed by anything but 0 or 1. at is the offset within path.

  • Missing(path~ : String, token~ : String)

    What the pointer names is not there: a name no member has, or an index no array reaches.

  • NotContainer(path~ : String, token~ : String)

    The pointer walked into something that is neither an object nor an array.

  • BadIndex(path~ : String, token~ : String)

    An array index that is not one — not decimal digits without a leading zero — or - where it does not mean append.

  • Document(why~ : String)

    The document is JSON but not a patch: not an array of operations.

  • Operation(at~ : Int32, why~ : String)

    The operation at at is not one: no op member, an op that is not one of the six, or a member it requires missing or of the wrong type.

  • TestFailed(path~ : String)

    A test that did not hold.

  • IntoChild(from~ : String, path~ : String)

    A move into one of the moved value's own children (RFC 6902 §4.4).

  • Root(path~ : String)

    The operation names the root, which has no parent to take it from.

Methods
  • pub fn Refused::at(Self) -> At?

    Where the failure happened, when it happened in text.

    None is not a failure to say: a pointer or a patch that was refused has a path rather than a line, and a line number invented for it would be worse than no line number at all.

  • pub fn Refused::equal(Self, Self) -> Bool

    Written by derive(Eq).

  • pub fn Refused::message(Self) -> String

    The failure as one sentence a person can read: what went wrong, and where.

    A text dialect names the line and column, counting from one, because that is how an editor counts; a binary format names the byte offset, because that is all a file without lines has to offer. The sentence is lower case and carries no full stop, so a caller can put it after its own word — "refused: " + error.message() — without the punctuation fighting.

  • pub fn Refused::not_equal(Self, Self) -> Bool

    Written by derive(Eq).

  • pub fn Refused::to_repr(Self) -> @debug.Repr

    Written by derive(Debug).

package

json

The grammar exactly: no comments, no trailing comma, no leading zero, no unquoted name, no control character inside a string.

dump

pub fn dump(Json, indent? : Int32, ascii? : Bool, sort? : Bool) -> Bytes

The same, as the UTF-8 bytes a body goes out as.

dumps

pub fn dumps(Json, indent? : Int32, ascii? : Bool, sort? : Bool) -> String

Write a tree as strict JSON, whatever dialect it was read from.

indent of zero is the compact form. ascii escapes everything above U+007E, for a transport that is not eight-bit clean — what is sometimes called ensure_ascii, except that it is off by default here, because a UTF-8 body is the normal case now. sort writes members in order of name instead of the order they were read in, which is what a diff wants.

load

pub fn load(BytesView, flavor? : @moonjson.Flavor, bom? : Bool) -> Json raise @moonjson.Refused

The same, from the UTF-8 bytes a body arrives as.

loads

pub fn loads(StringView, flavor? : @moonjson.Flavor) -> Json raise @moonjson.Refused

Read one JSON document.

Strictly RFC 8259: a leading zero, a trailing comma, a comment or a control character inside a string is an error naming the line and column it is on, not something quietly accepted.

package

jsonc

JSON plus // and /* */ comments and a trailing comma. Nothing else: a name is still quoted, a string is still double-quoted.

dump

pub fn dump(Json, indent? : Int32, ascii? : Bool, sort? : Bool) -> Bytes

The same, as the UTF-8 bytes a body goes out as.

dumps

pub fn dumps(Json, indent? : Int32, ascii? : Bool, sort? : Bool) -> String

Write a tree as strict JSON, whatever dialect it was read from.

indent of zero is the compact form. ascii escapes everything above U+007E, for a transport that is not eight-bit clean — what is sometimes called ensure_ascii, except that it is off by default here, because a UTF-8 body is the normal case now. sort writes members in order of name instead of the order they were read in, which is what a diff wants.

load

pub fn load(BytesView, flavor? : @moonjson.Flavor, bom? : Bool) -> Json raise @moonjson.Refused

The same, from the UTF-8 bytes a body arrives as.

loads

pub fn loads(StringView, flavor? : @moonjson.Flavor) -> Json raise @moonjson.Refused

Read one JSONC document: JSON, plus comments and a trailing comma.

This is what tsconfig.json and settings.json are written in. Comments are discarded — the tree has nowhere to put them — so writing the result back out loses them.

package

json5

Everything ECMAScript 5.1 would have accepted: comments, trailing commas, unquoted names, single quotes, hexadecimal and signed numbers, Infinity and NaN, and strings continued across lines.

dump

pub fn dump(Json, indent? : Int32, ascii? : Bool, sort? : Bool) -> Bytes

The same, as the UTF-8 bytes a body goes out as.

dumps

pub fn dumps(Json, indent? : Int32, ascii? : Bool, sort? : Bool) -> String

Write a tree as strict JSON, whatever dialect it was read from.

indent of zero is the compact form. ascii escapes everything above U+007E, for a transport that is not eight-bit clean — what is sometimes called ensure_ascii, except that it is off by default here, because a UTF-8 body is the normal case now. sort writes members in order of name instead of the order they were read in, which is what a diff wants.

load

pub fn load(BytesView, flavor? : @moonjson.Flavor, bom? : Bool) -> Json raise @moonjson.Refused

The same, from the UTF-8 bytes a body arrives as.

loads

pub fn loads(StringView, flavor? : @moonjson.Flavor) -> Json raise @moonjson.Refused

Read one JSON5 document.

JSON5 admits values JSON cannot write — Infinity, NaN, a hexadecimal integer — so a document read here and written as strict JSON is not always the same document. write says what becomes of each.

package

lines

One document to a line, separated by a line feed, each one complete. It is what a log file or a stream of records is written as, because a reader can start anywhere and a writer can append without rewriting.

dump_all

pub fn dump_all(ArrayView[Json], ascii? : Bool, sort? : Bool) -> Bytes

The same, as UTF-8 bytes.

dumps_all

pub fn dumps_all(ArrayView[Json], ascii? : Bool, sort? : Bool) -> String

Write documents one to a line, each ending in a line feed.

Every line ends in one, including the last: a file that ends without it cannot be appended to safely, which is the format's whole purpose.

load_all

pub fn load_all(BytesView, flavor? : @moonjson.Flavor, bom? : Bool) -> Array[Json] raise @moonjson.Refused

The same, from bytes.

loads_all

pub fn loads_all(StringView, flavor? : @moonjson.Flavor) -> Array[Json] raise @moonjson.Refused

Read a stream of documents, one to a line.

A blank line is skipped rather than refused: files grown by appending end up with one, and refusing it would make a reader fail on its own writer's output. A carriage return before the line feed is also tolerated, because half the tools that produce these files run on Windows.

package

pointer

A string that names one place in a document. ~1 stands for a slash and ~0 for a tilde, in that order — undoing them the other way round would turn ~01 into a slash.

escape

pub fn escape(StringView) -> String

Write a name so it can go into a pointer: ~ first, then /.

get

pub fn get(Json, StringView) -> Json raise @moonjson.Refused

Follow a pointer and return what is there.

- as an array index is refused here even though RFC 6901 §4 names it: it means "one past the last element", which exists only for JSON Patch's add, and returning something for it would be inventing a value.

has

pub fn has(Json, StringView) -> Bool

Whether a pointer leads anywhere. A malformed pointer is not an exception here — it simply does not lead anywhere.

join

pub fn join(ArrayView[String]) -> String

Join names into a pointer, escaping each.

remove

pub fn remove(Json, StringView) -> Json raise @moonjson.Refused

Take away whatever pointer leads to, and answer the document that results.

RFC 6902's remove: the member or the element must be there, because taking away what was never there is a request nobody can have meant. The root cannot be removed — an empty pointer names the whole document, and there would be nothing left to answer with.

set

pub fn set(Json, StringView, Json) -> Json raise @moonjson.Refused

Put value where pointer leads, and answer the document that results.

The semantics are RFC 6902's add and replace taken together: a member or an element that is already there is replaced, one that is not is created, and - as an array index appends — which is the one place RFC 6901 §4 says that token means something, and the reason get refuses it.

It answers a new document rather than changing the one it was given. A tree is shared: a value reached through two pointers is one value, and editing it in place would change both. Only the path from the root to the edit is rebuilt; everything else is the same value it was.

moonbit nocheck
let cfg = @pointer.set(cfg, "/spec/replicas", Json::number(5))

tokens

pub fn tokens(StringView) -> Array[String] raise @moonjson.Refused

Split a pointer into the names it walks through, undoing the escapes.

The empty pointer names the whole document and yields no tokens; "/" names the member whose name is empty and yields one.

package

patch

A patch document is an array of operation objects, each naming one change — add, remove, replace, move, copy or test — against a target document. It is read into Operations rather than left as a tree of objects, because the six share nothing but their shape: two carry a value, two carry a second pointer, and a document whose op is misspelled is a document that would quietly do nothing.

apply

pub fn apply(Json, ArrayView[Operation]) -> Json raise @moonjson.Refused

Apply a patch to a document, and answer the document that results.

The operations are applied in order, each to the result of the one before it. A patch that fails partway applies nothing: the document it was given is never changed, and the answer is the error rather than half a document — RFC 6902 §5 says that if any operation is unsuccessful, application of the entire patch document is not deemed successful.

It answers a new document rather than changing the one it was given, for the same reason @pointer.set does: a tree is shared, and a value reached through two pointers is one value.

dump

pub fn dump(ArrayView[Operation], indent? : Int32, ascii? : Bool, sort? : Bool) -> Bytes

The same, as the UTF-8 bytes a body goes out as.

dumps

pub fn dumps(ArrayView[Operation], indent? : Int32, ascii? : Bool, sort? : Bool) -> String

Write a patch document: the operations as the array of objects RFC 6902 §4 prints them, op first and from before path where a move or a copy has both.

load

pub fn load(BytesView, flavor? : @moonjson.Flavor, bom? : Bool) -> Array[Operation] raise @moonjson.Refused

The same, from the UTF-8 bytes a body arrives as.

loads

pub fn loads(StringView, flavor? : @moonjson.Flavor) -> Array[Operation] raise @moonjson.Refused

Read a patch document: the array of operations it names.

The document itself is read as strict JSON — a patch is a protocol message rather than a settings file — and the dialect can still be named for a caller whose patches arrive from a JSONC source.

Operation

pub(all) enum Operation

One operation in a patch document, as RFC 6902 §4 names the six.

Variants
  • Add(path~ : String, value~ : Json)

    Put value where path leads (RFC 6902 §4.1).

    An object member that is not there is created, one that is there is replaced, and an array element is inserted — the elements at or above the index shift one place to the right — with - appending at the end. The root is a target too: an add to the empty pointer makes the value the whole document.

  • Remove(path~ : String)

    Take away what path leads to (RFC 6902 §4.2).

    It has to be there: taking away what was never there is a request nobody can have meant. The root cannot be taken away — the empty pointer names the whole document, and there would be nothing left to answer with.

  • Replace(path~ : String, value~ : Json)

    Replace the value at path (RFC 6902 §4.3).

    The target has to be there already; this is add without the creating.

  • Move(path~ : String, from~ : String)

    Take the value at from away and put it at path (RFC 6902 §4.4).

    from must not be a parent of path: a location cannot be moved into one of its own children.

  • Copy(path~ : String, from~ : String)

    Copy the value at from to path (RFC 6902 §4.5).

  • Test(path~ : String, value~ : Json)

    Require the value at path to equal value (RFC 6902 §4.6).

    Equality is the tree's: a number equals the same number written another way, because the tree holds a number as a number and not as text.

Methods
  • pub fn Operation::equal(Self, Self) -> Bool

    Written by derive(Eq).

  • pub fn Operation::not_equal(Self, Self) -> Bool

    Written by derive(Eq).

  • pub fn Operation::to_repr(Self) -> @debug.Repr

    Written by derive(Debug).

package

merge

A merge patch has no syntax of its own: the patch document is a JSON document, read with @json.loads like any other. What this package adds is the evaluation — the rules that say which members of the target are added, replaced or taken away — so there are no loads and dumps here. The four faces belong to formats, and the format is plain JSON.

merge

pub fn merge(Json, Json) -> Json

Apply a merge patch to a document, and answer the document that results.

A merge patch looks like the document it changes rather than like a list of instructions: a member that is not there is added, one that is there is replaced, and a member whose value is null is taken away — a null in the patch is an instruction, while a null already in the target is data and stays. A patch that is not an object replaces the whole document, null itself among them, and a target that is not an object is ignored and started again from an empty one.

It answers a new document rather than changing the one it was given, for the same reason @pointer.set does: a tree is shared, and a value reached through two pointers is one value.

moonbit nocheck
let cfg = @json.loads("{\"host\":\"localhost\",\"port\":8080,\"tls\":false}")
@merge.merge(cfg, @json.loads("{\"port\":9090,\"tls\":null}"))
// {"host":"localhost","port":9090}
package

path

A query is compiled once into selectors and then run against any number of documents: a string on every call would parse the same query for every node it touches, and a filter is evaluated once per node.

alike

pub fn alike(Json, Json) -> Bool

Equality of values: numbers by value — 1 and 1.0 are one number — and everything else structurally.

compile

pub fn compile(StringView) -> Path raise @moonjson.Refused

Compile a query.

The grammar is RFC 9535 §2.2's, and a query that does not parse is refused with the offset it stopped at rather than run as something else. Nothing around the query is whitespace: " $" and "$ " are not queries.

finds

pub fn finds(StringView, StringView) -> Bool raise @moonjson.Refused

Whether the pattern matches any part of the text.

matches

pub fn matches(StringView, StringView) -> Bool raise @moonjson.Refused

Whether the pattern matches the whole of the text.

The dialect is I-Regexp (RFC 9485): ., the classes [...], \p{...} for the Unicode general categories, the four quantifiers, alternation and groups. \d, \w and \s and their negations are accepted as well, which I-Regexp itself leaves out; they are what a pattern written for another dialect reaches for first, and refusing them would be a surprise rather than a service.

select

pub fn select(Json, StringView) -> Array[Json] raise @moonjson.Refused

Run a query against a document, answering the nodes it selects in document order.

Op

pub(all) enum Op

A comparable operator.

Variants
  • Eq

    Equal.

  • Ne

    Not equal.

  • Lt

    Less than.

  • Le

    Less than or equal.

  • Gt

    Greater than.

  • Ge

    Greater than or equal.

Methods
  • pub fn Op::equal(Self, Self) -> Bool

    Written by derive(Eq).

  • pub fn Op::not_equal(Self, Self) -> Bool

    Written by derive(Eq).

  • pub fn Op::to_repr(Self) -> @debug.Repr

    Written by derive(Debug).

Operand

pub(all) enum Operand

One side of a comparison, or the thing a test asks about.

Variants
  • Const(Json)

    A literal written in the query.

  • Nodes(root~ : Bool, segments~ : Array[Segment])

    A query: @ for the node under test, $ for the document.

  • Call(name~ : String, args~ : Array[Operand])

    One of the five functions RFC 9535 §2.4 defines.

Path

pub(all) struct Path

A compiled query.

Fields
  • segments : Array[Segment]

    The segments, from the first to the last.

Methods
  • pub fn Path::select(Self, Json) -> Array[Json]

    Run a compiled query against a document, answering the nodes it selects in document order. The same Path can be run against as many documents as you like, which is the reason to compile one.

Segment

pub(all) enum Segment

One step of a query.

Variants
  • Child(Array[Selector])

    The selectors applied to the node itself.

  • Descendant(Array[Selector])

    The selectors applied to the node and to every node inside it, in document order — the .. segment.

Selector

pub(all) enum Selector

One way of choosing from a node.

Variants
  • Name(String)

    A member with this name, or nothing.

  • Index(Int64)

    An element at this index, or nothing.

    An index is an I-JSON integer, which RFC 9535 §2.3.3 puts at ±(2^53 − 1) — wider than Int32, and wider than any array that can exist, which is why an index past the end selects nothing rather than being refused.

  • Wildcard

    Every element of an array, or every member of an object.

  • Slice(start~ : Int64?, end~ : Int64?, step~ : Int64?)

    A range of an array. None means "from the beginning", "to the end", or "by one" — the three defaults RFC 9535 §2.3.4.2 gives the three parts.

  • Filter(Test)

    The nodes for which the expression holds.

Test

pub(all) enum Test

What a filter asks.

Variants
  • Or(Test, Test)

    Either side holds.

  • And(Test, Test)

    Both sides hold.

  • Not(Test)

    The test does not hold.

  • Exists(Operand)

    Whether the query selects anything at all.

  • Compare(Operand, Op, Operand)

    Whether two values compare as the operator says.

package

jcs

One tree, one document: JCS exists so that two parties can sign the same bytes. Nothing here is a knob, because a canonical form with options is not canonical — the numbers are ECMAScript's, the member order is UTF-16 code unit order, and a number that is not finite is refused rather than spelled some way we invented.

dump

pub fn dump(Json) -> Bytes raise @moonjson.Refused

The same, as the UTF-8 bytes a signature is taken over.

dumps

pub fn dumps(Json) -> String raise @moonjson.Refused

The canonical form of a tree, as RFC 8785 writes it.

Members are ordered by their names compared as UTF-16 code units — not as code points, which differ for anything above U+FFFF — numbers are written the way ECMAScript writes them, and nothing is escaped that does not have to be. Two trees that hold the same values therefore come out as the same bytes, which is the whole point: a signature over these bytes is a signature over the document.

A number that is not finite cannot be written, because the RFC's number serialisation is ECMAScript's and ECMAScript has no syntax for it. It is refused rather than replaced with null or 0, either of which would sign something the document does not say.

package

cbor

CBOR is JSON's data model with the corners filled in: byte strings, integers that a double cannot hold, tags, and a distinction between false and undefined. It writes itself into a tree the rest of this family already understands, and what the tree cannot hold outright is carried in the markers documented on load — so a CBOR document can be read here and written as JSON, and a JSON document can be written here and read there.

dump

pub fn dump(Json, policy? : Policy) -> Bytes raise @moonjson.Refused

Write a tree as CBOR.

load

pub fn load(BytesView, policy? : Policy) -> Json raise @moonjson.Refused

What a tree becomes when a CBOR value has no JSON counterpart.

JSON has no byte strings, no tags, no undefined and no map keys that are not strings, so those four are carried as objects with one reserved member each. They are the same markers every other binary package in this family uses, so a value read from CBOR and written to MessagePack keeps its bytes:

CBORtree
h'0102'{"$bytes": "0102"} — lowercase hex, no separators
1(1363896240){"$tag": 1, "value": 1363896240}
undefined{"$undefined": true}
simple(16){"$simple": 16}
{1: 2}{"$map": [[1, 2]]} — any map with a key that is not a string

A map whose keys are all strings is written as a plain object, and a plain object is read back as a map. A document that means {"$bytes": "00"} as data has to be written in the $map form, because the marker is reserved — the same bargain MongoDB's Extended JSON makes, and for the same reason: a tree with one object type needs one reserved name.

Writing also accepts these markers: an object with exactly one $bytes member becomes a byte string, and so on for the rest. What was read from CBOR is therefore written back as the same CBOR, and MessagePack's byte strings, BSON's binary and CBOR's are one thing in the tree.

policy

pub let policy : Policy

The defaults: deterministic, five hundred levels deep.

Policy

pub(all) struct Policy

How CBOR is read and written.

Two knobs, both of them things the RFC leaves open rather than things we invented: whether to write the deterministic form, and how deep a document may nest before it is refused.

Fields
  • deterministic : Bool

    Write the deterministic encoding of RFC 8949 §4.2.3: the shortest form for every length and every number, floats in the shortest width that reads back as the same value, and map keys in bytewise order of their encodings.

    It is on by default because that is what the RFC §4.2 recommends and what a signature or a hash over the encoding needs. Turning it off still writes valid CBOR — every float becomes a double and map keys keep the order the tree has — which is what a reader that does not care about byte identity may prefer, because it is cheaper.

  • depth : Int32

    How deep nesting may go. The same five hundred the text reader allows, and for the same reason: a document of a hundred thousand open brackets is a denial of service rather than a document.

Methods
  • pub fn Policy::equal(Self, Self) -> Bool

    Written by derive(Eq).

  • pub fn Policy::new(deterministic? : Bool, depth? : Int32) -> Self

    Build a policy by naming the parts that differ from the defaults.

  • pub fn Policy::not_equal(Self, Self) -> Bool

    Written by derive(Eq).

  • pub fn Policy::to_repr(Self) -> @debug.Repr

    Written by derive(Debug).

package

msgpack

MessagePack is CBOR's older cousin: the same data model, a different header, and no tags. Two things it has that JSON has not are a byte string and an extension type, and both are carried in the markers documented on load — the same markers CBOR uses for bytes, so a document moved from one to the other keeps its bytes.

dump

pub fn dump(Json, policy? : Policy) -> Bytes raise @moonjson.Refused

Write a tree as MessagePack.

load

pub fn load(BytesView, policy? : Policy) -> Json raise @moonjson.Refused

Read one MessagePack document.

What JSON cannot hold comes back as the family's markers:

MessagePacktree
bin{"$bytes": "0102"} — lowercase hex
ext{"$ext": 1, "data": "0102"} — the type as a number
an integer above 2^53a number that keeps its digits as text

The same markers are read back as extensions and byte strings, so a document survives a round trip through this package.

policy

pub let policy : Policy

The defaults: doubles, five hundred levels.

Floats

pub(all) enum Floats

How wide a float is written.

Variants
  • Shortest

    32 bits when the value comes back unchanged, 64 when it does not.

  • Double

    Always 64 bits.

Methods
  • pub fn Floats::equal(Self, Self) -> Bool

    Written by derive(Eq).

  • pub fn Floats::not_equal(Self, Self) -> Bool

    Written by derive(Eq).

  • pub fn Floats::to_repr(Self) -> @debug.Repr

    Written by derive(Debug).

Policy

pub(all) struct Policy

How MessagePack is read and written.

Fields
  • floats : Floats

    MessagePack's reference implementation writes every float as a double unless it is told otherwise, so that is the default: a 32-bit float that reads back the same is smaller but is not what the other side expects to see.

  • depth : Int32

    How deep nesting may go before the document is refused.

Methods
  • pub fn Policy::equal(Self, Self) -> Bool

    Written by derive(Eq).

  • pub fn Policy::new(floats? : Floats, depth? : Int32) -> Self

    Build a policy by naming the parts that differ from the defaults.

  • pub fn Policy::not_equal(Self, Self) -> Bool

    Written by derive(Eq).

  • pub fn Policy::to_repr(Self) -> @debug.Repr

    Written by derive(Debug).

package

bson

BSON is what MongoDB stores and puts on the wire: little-endian, length-first, and richer than JSON by eleven types. Every one of them has a marker here, so nothing it carries is lost — an ObjectId stays twelve bytes, a decimal128 stays sixteen, and a timestamp stays two 32-bit halves.

dump

pub fn dump(Json, policy? : Policy) -> Bytes raise @moonjson.Refused

Write a tree as a BSON document.

load

pub fn load(BytesView, policy? : Policy) -> Json raise @moonjson.Refused

Read one BSON document.

A BSON document is always an object at the top, and the types JSON has no syntax for come back as markers:

BSONtree
binary, subtype 0{"$bytes": "0102"}
binary, any other subtype{"$binary": {"kind": 4, "data": "0102"}}
ObjectId{"$oid": "507f1f77bcf86cd799439011"}
UTC datetime{"$date": 1356350400000} — milliseconds
regular expression{"$regex": "^a", "options": "i"}
DBPointer{"$dbpointer": {"ns": "db.c", "oid": "…"}}
JavaScript{"$code": "…"}
JavaScript with scope{"$code": "…", "$scope": {…}}
symbol{"$symbol": "…"}
timestamp{"$timestamp": {"t": 1, "i": 2}}
decimal128{"$decimal128": "…32 hex digits…"}
undefined{"$undefined": true}
MinKey / MaxKey{"$minKey": true} / {"$maxKey": true}

Writing reads the same markers back, so a document that goes through the tree comes out as it went in. A marker is a reserved name: an object whose only member is $oid is an ObjectId, not a document that happens to have that member.

policy

pub let policy : Policy

The defaults: five hundred levels.

Policy

pub(all) struct Policy

How BSON is read and written.

Fields
  • depth : Int32

    How deep a document may nest. BSON documents nest inside documents, and a chain of them is as good a way to exhaust a stack as a chain of brackets.

Methods
  • pub fn Policy::equal(Self, Self) -> Bool

    Written by derive(Eq).

  • pub fn Policy::new(depth? : Int32) -> Self

    BSON writes int32 and int64 as two types and the tree holds one number, so a number is written in the narrowest width that holds it — the same choice the reference implementations make when they are handed a language integer rather than a typed one. The value is the same; only the width differs, and int32 is what MongoDB itself uses below two billion.

  • pub fn Policy::not_equal(Self, Self) -> Bool

    Written by derive(Eq).

  • pub fn Policy::to_repr(Self) -> @debug.Repr

    Written by derive(Debug).

package

ubjson

UBJSON is JSON's data model exactly: null, true, false, numbers, strings, arrays and objects, with no byte strings, no tags, no undefined and no non-string map keys. A UBJSON document reads into the same tree the rest of this family already understands, and what the tree cannot hold outright is carried in the markers documented on load — written, where UBJSON allows, as the closest plain thing.

dump

pub fn dump(Json, policy? : Policy) -> Bytes raise @moonjson.Refused

Write a tree as UBJSON, in the simple (no length-prefix) form.

load

pub fn load(BytesView, policy? : Policy) -> Json raise @moonjson.Refused

What a tree becomes when a UBJSON value has no JSON counterpart.

UBJSON *is* JSON's data model — null, true, false, numbers, strings, arrays and objects, with no byte strings, no tags, no undefined and no non-string map keys — so most of the markers the rest of this family uses have no UBJSON form. They are documented here, and what UBJSON cannot carry is either written as the closest plain thing or, for bytes, as an array:

UBJSONtree
blob{"$bytes": "0102"} — written as a UBJSON array [U 01, U 02] of uint8; reading that array back yields a plain array, not the marker — UBJSON has no byte string, so the marker is one-way (documented, not round-tripped)
1(1363896240){"$tag": 1, "value": 1363896240} — written as a plain object with those members
{1: 2}{"$map": [[1, 2]]} — written as a plain object
simple(16){"$simple": 16} — written as a plain object
undefined{"$undefined": true} — written as a plain object

A document that means {"$bytes": "00"} as data therefore has to be written in the $map form, because the marker is reserved — the same bargain every other package in this family makes, here reduced to a single marker that has only a one-way encoding. The other four markers ($tag, $map, $simple, $undefined) are written as plain objects with those member names, because UBJSON has no native form for them at all.

Writing also accepts these markers: an object with exactly one $bytes member becomes a UBJSON array of uint8, so a byte string read elsewhere and written here is carried as closely as UBJSON allows. What a real UBJSON document needs — numbers, strings, arrays, objects — needs no marker in the tree at all, and the non-JSON markers above are only round-tripped between the other packages of this family, never through UBJSON itself.

Encoding is the canonical, length-free "simple" form of the spec: containers are opened with [/{ and closed with ]/} with a per-element type marker, never the optimized # (count) or $ (type) prefixes. It is spec-based and is not byte-validated against an external UBJSON library, because none is available in this workspace.

policy

pub let policy : Policy

The default: five hundred levels deep.

Policy

pub(all) struct Policy

How UBJSON is read and written.

The one knob the format leaves open is how deep a document may nest before it is refused, because a document of a hundred thousand open brackets is a denial of service rather than a document.

Fields
  • depth : Int32

    How deep nesting may go. The same five hundred the text reader allows, and for the same reason: a document of a hundred thousand open brackets is a denial of service rather than a document.

Methods
  • pub fn Policy::equal(Self, Self) -> Bool

    Written by derive(Eq).

  • pub fn Policy::new(depth? : Int32) -> Self

    Build a policy by naming the parts that differ from the default.

  • pub fn Policy::not_equal(Self, Self) -> Bool

    Written by derive(Eq).

  • pub fn Policy::to_repr(Self) -> @debug.Repr

    Written by derive(Debug).

package

smile

Smile is JSON's data model with one addition, a byte string, written as a 4-byte :)\n\0 header, then one value, then a 0x00 end marker. A Smile blob reads into the same tree the rest of this family already understands, and what the tree cannot hold outright is carried in the markers documented on load — so a blob read here is written as a blob again, and the byte strings of CBOR, MessagePack and BSON are the one $bytes in the tree.

dump

pub fn dump(Json, policy? : Policy) -> Bytes raise @moonjson.Refused

Write a tree as Smile.

load

pub fn load(BytesView, policy? : Policy) -> Json raise @moonjson.Refused

What a tree becomes when a Smile value has no JSON counterpart.

JSON has no byte string and no notion of a tag, undefined or a non-string key, so those four are carried as objects with one reserved member each, the same markers every other binary package in this family uses, so a value read from Smile and written to CBOR keeps its meaning:

Smiletree
binary{"$bytes": "0102"} — lowercase hex, no separators
tag(n, v){"$tag": n, "value": v}
{k: v} with a non-string key{"$map": [[k, v], ...]}
simple(n){"$simple": n}
undefined{"$undefined": true}

Of these, only $bytes has a native Smile form: Smile's binary value. So the encoder turns a single-member {"$bytes": "hex"} object into a Smile binary, and the decoder turns a Smile binary back into {"$bytes": "hex"}. The remaining four markers ($tag, $map, $simple, $undefined) have no native Smile encoding; they are written as the plain objects they are and read back as plain objects, so a tree that already carries one round-trips without it being mistaken for something else.

policy

pub let policy : Policy

The default: five hundred levels deep.

Policy

pub(all) struct Policy

How Smile is read and written.

The byte layout itself is fixed by the spec; the one knob is how deep a document may nest before it is refused, because a document of a hundred thousand open brackets is a denial of service rather than a document.

Fields
  • depth : Int32

    The deepest nesting accepted, in levels.

Methods
  • pub fn Policy::equal(Self, Self) -> Bool

    Written by derive(Eq).

  • pub fn Policy::new(depth? : Int32) -> Self

    Build a policy by naming the parts that differ from the default.

  • pub fn Policy::not_equal(Self, Self) -> Bool

    Written by derive(Eq).

  • pub fn Policy::to_repr(Self) -> @debug.Repr

    Written by derive(Debug).

package

bjson

bjson — "Binary JSON".

dump

pub fn dump(Json, policy? : Policy) -> Bytes raise @moonjson.Refused

Write a tree as bjson.

A value of any shape is accepted at the top level. The types JSON has no syntax for are written through the $bytes and $undefined markers documented on load, so a tree read from another binary package survives the round trip.

load

pub fn load(BytesView, policy? : Policy) -> Json raise @moonjson.Refused

Read one bjson document from its bytes.

bjson is binary JSON, so the one type JSON has no syntax for — raw bytes — comes back through the same markers every binary package in this family uses, so a value read here and written to CBOR or BSON keeps its bytes:

bjsontree
0x0B–0x0D binary{"$bytes": "0102"} — lowercase hex, no separators
0x10 undefined{"$undefined": true}

(The full marker set used across the family is $bytes, $tag, $map, $simple, $undefined; this minimal reader accepts $bytes and $undefined, which are the ones a binary-JSON round trip needs.)

policy

pub let policy : Policy

The default: five hundred levels deep.

Policy

pub(all) struct Policy

How bjson is read and written.

Fields
  • depth : Int32

    How deep nesting may go, shared with the rest of the family. A document of a hundred thousand open containers is a denial of service rather than a document, so the same five hundred the text reader allows is the default.

Methods
  • pub fn Policy::equal(Self, Self) -> Bool

    Written by derive(Eq).

  • pub fn Policy::new(depth? : Int32) -> Self

    Build a policy naming the depth that differs from the default.

  • pub fn Policy::not_equal(Self, Self) -> Bool

    Written by derive(Eq).

  • pub fn Policy::to_repr(Self) -> @debug.Repr

    Written by derive(Debug).

package

krson

KRSON is a real, schema-first binary serialization format. The authoritative SPEC.md the project references was not publicly retrievable at the time of writing and the authors state the wire format "may still change between minor versions", so this package implements the self-describing *schemaless* subset (magic "KR", a string interning table, varint + zigzag integers, big-endian float64) as a genuine encoder/decoder. See krson.mbt for the full design note and fidelity warning.

dump

pub fn dump(Json, policy? : Policy) -> Bytes raise @moonjson.Refused

Write a Json tree as a KRSON schemaless document.

See load for the design note and the fidelity warning. The writer builds the string-interning table from every object key and string value in the tree, writes the KR header with a zero schema id, the table, then the value tree using the tags below. A value the format cannot carry as JSON — there is none in the JSON-native model — would raise @moonjson.Unwritable.

tagtree
0x00null
0x01 + 0/1true / false
0x02 + zigzag varintan integer
0x03 + 8 bytes (big-endian)a float64
0x04 + varinta string, by index into the string table
0x05 + varint + itemsan array
0x06 + varint + (key-index, value) pairsan object

load

pub fn load(BytesView, policy? : Policy) -> Json raise @moonjson.Refused

Read a KRSON document and turn it into a Json tree.

KRSON is a real, schema-first binary serialization format (Rust core krson-core, JS SDK krson-js). Its schema-first form carries a 4-byte schema id and needs the originating schema runtime to interpret — a thing the shared Json tree here cannot host, and one that is not portable on its own. This package therefore implements the self-describing schemaless record, which the SDK also emits (encode/decode with no schema): magic KR, a version and flags byte, a schema id of 0, a string-interning table (every object key and string value is written once and referred to by index), then the value tree.

WARNING ON FIDELITY — the authoritative SPEC.md referenced by the KRSON project was not publicly retrievable at the time of writing, and the authors state the wire format "may still change between minor versions". This codec follows the primitives the SDK documents (magic KR, a length-prefixed string table, varint + zigzag integers, big-endian float64, and the value tags below). It is a genuine, self-consistent encoder/decoder of that subset; byte-exact interop with upstream KRSON is best-effort.

The value model is JSON-native, so this reader never produces the $bytes, $tag, $map, $simple or $undefined markers the other binary packages in this family use: a KRSON document has no byte strings, tags, maps with non-string keys, undefined or simple values to carry.

policy

pub let policy : Policy

The defaults: keys in source order, five hundred levels deep.

Policy

pub(all) struct Policy

How KRSON is read and written.

Two knobs, both things the format leaves to the caller rather than things we invented: whether object members are written in sorted key order, and how deep a document may nest before it is refused.

Fields
  • deterministic : Bool

    Write object members in bytewise order of their key. KRSON is a self-describing format, so the order never changes what is read back, but a caller that wants a stable byte image (for a hash or a signature) turns this on.

  • depth : Int32

    How deep nesting may go. The same five hundred the text reader allows, and for the same reason: a document of a hundred thousand open brackets is a denial of service rather than a document.

Methods
  • pub fn Policy::equal(Self, Self) -> Bool

    Written by derive(Eq).

  • pub fn Policy::new(deterministic? : Bool, depth? : Int32) -> Self

    Build a policy by naming the parts that differ from the defaults.

  • pub fn Policy::not_equal(Self, Self) -> Bool

    Written by derive(Eq).

  • pub fn Policy::to_repr(Self) -> @debug.Repr

    Written by derive(Debug).

package

cbd

A compact binary encoding of the JSON data model: a 5-byte header, then each value as a one-byte descriptor whose low 3 bits are the type and whose high 5 bits hold a short length, with a varint escape for longer ones. Numbers are kept as IEEE-754 bits and a byte string as the reserved {"$bytes": "…"}.

dump

pub fn dump(Json, policy? : Policy) -> Bytes raise @moonjson.Refused

Write a tree as CBD.

The dictionary is built in the order keys are first met, matching the specification's "order of first appearance".

load

pub fn load(BytesView, policy? : Policy) -> Json raise @moonjson.Refused

Read one CBD document.

The header is magic 0xCB 0xD1, version 0x01, then the dictionary size. A byte string has no type of its own in CBD v0.1.0, so it stays an ordinary object {"$bytes": "…"} — the marker every package in this family shares — and a trip through CBD leaves it exactly as it was.

policy

pub let policy : Policy

The default: five hundred levels deep.

Policy

pub(all) struct Policy

How CBD is read and written.

The byte layout is fixed; the one knob is how deep a document may nest before it is refused, because a document of a hundred thousand open brackets is a denial of service rather than a document.

Fields
  • depth : Int32

    The deepest nesting accepted, in levels.

Methods
  • pub fn Policy::equal(Self, Self) -> Bool

    Written by derive(Eq).

  • pub fn Policy::new(depth? : Int32) -> Self

    Build a policy by naming the parts that differ from the default.

  • pub fn Policy::not_equal(Self, Self) -> Bool

    Written by derive(Eq).

  • pub fn Policy::to_repr(Self) -> @debug.Repr

    Written by derive(Debug).

package

sqlite

jsonb is JSON's data model with one addition, a byte string, so a SQLite blob reads into the same tree the rest of this family already understands, and what the tree cannot hold outright is carried in the markers documented on load.

dump

pub fn dump(Json, policy? : Policy) -> Bytes raise @moonjson.Refused

Write a tree as SQLite jsonb.

load

pub fn load(BytesView, policy? : Policy) -> Json raise @moonjson.Refused

What a tree becomes when a SQLite jsonb value has no JSON counterpart.

JSON has no byte string, so a SQLite blob is carried as an object with one reserved member:

jsonbtree
blob{"$bytes": "0102"} — lowercase hex, no separators

SQLite jsonb is JSON's data model plus that one addition, so a blob reads into the same tree the rest of this family already understands, and what the tree cannot hold outright is carried in the markers documented here. A blob read here is written as a blob again, and the byte strings of CBOR, MessagePack and BSON are the one $bytes in the tree.

An object that happens to have a single $bytes member as its *data* has to be written through the $map form, because the marker is reserved — the same bargain every other package in this family makes.

policy

pub let policy : Policy

The default: five hundred levels deep.

Policy

pub(all) struct Policy

How SQLite jsonb is read and written.

The byte layout itself is fixed by SQLite; the one knob is how deep a document may nest before it is refused, because a document of a hundred thousand open brackets is a denial of service rather than a document.

Fields
  • depth : Int32

    The deepest nesting accepted, in levels.

Methods
  • pub fn Policy::equal(Self, Self) -> Bool

    Written by derive(Eq).

  • pub fn Policy::new(depth? : Int32) -> Self

    Build a policy by naming the parts that differ from the default.

  • pub fn Policy::not_equal(Self, Self) -> Bool

    Written by derive(Eq).

  • pub fn Policy::to_repr(Self) -> @debug.Repr

    Written by derive(Debug).

package

pg

The layout follows PostgreSQL's on-disk jsonb shape (src/include/utils/jsonb.h): a length/type header word, an element-count word, a JEntry per element, and a data region. Numbers are kept as IEEE-754 bits so a tree round-trips exactly.

dump

pub fn dump(Json, policy? : Policy) -> Bytes raise @moonjson.Refused

Write a tree as PostgreSQL jsonb.

load

pub fn load(BytesView, policy? : Policy) -> Json raise @moonjson.Refused

What a tree becomes when a jsonb value has no JSON counterpart.

jsonbtree
blob{"$bytes": "0102"} — lowercase hex, no separators

A byte string survives a trip through this format the same way it does through SQLite jsonb, so the byte strings of CBOR, MessagePack and BSON are one $bytes in the tree whatever format carried them. A number is kept as PostgreSQL's NumericVar, an exact base-10000 decimal, so it round-trips digit for digit.

policy

pub let policy : Policy

The default: five hundred levels deep.

Policy

pub(all) struct Policy

How PostgreSQL jsonb is read and written.

The byte layout is fixed; the one knob is how deep a document may nest before it is refused, because a document of a hundred thousand open brackets is a denial of service rather than a document.

Fields
  • depth : Int32

    The deepest nesting accepted, in levels.

Methods
  • pub fn Policy::equal(Self, Self) -> Bool

    Written by derive(Eq).

  • pub fn Policy::new(depth? : Int32) -> Self

    Build a policy by naming the parts that differ from the default.

  • pub fn Policy::not_equal(Self, Self) -> Bool

    Written by derive(Eq).

  • pub fn Policy::to_repr(Self) -> @debug.Repr

    Written by derive(Debug).

package

superjson

A JSON superset that wraps a value in a {"json": <value>, "meta": {...}} envelope carrying the type of each nested value — Date, BigInt, Map, Set, and so on — so the receiver can rebuild the JavaScript objects the JSON wire form cannot name.

dump

pub fn dump(Json, indent? : Int32, ascii? : Bool, sort? : Bool) -> Bytes

The same, as the UTF-8 bytes a body goes out as.

Writes the tree — including any superjson json/meta envelope — as ordinary JSON, delegating to @moonjson.dump.

dumps

pub fn dumps(Json, indent? : Int32, ascii? : Bool, sort? : Bool) -> String

Write a tree as strict JSON, whatever dialect it was read from.

A superjson payload is JSON, so writing one is just ordinary JSON output (delegating to @moonjson.dumps). The envelope — json and meta — is emitted exactly as read. indent of zero is the compact form. ascii escapes everything above U+007E, for a transport that is not eight-bit clean. sort writes members in order of name instead of the order they were read in, which is what a diff wants.

load

pub fn load(BytesView, flavor? : @moonjson.Flavor, bom? : Bool) -> Json raise @moonjson.Refused

The same, from the UTF-8 bytes a body arrives as.

Reads the superjson envelope as ordinary JSON, preserving both the json payload and the meta type table verbatim. See loads for why this package does not itself reconstruct JavaScript types: the Json tree has none to reconstruct into, and the wire form is plain JSON regardless.

loads

pub fn loads(StringView, flavor? : @moonjson.Flavor) -> Json raise @moonjson.Refused

Read one superjson document.

Superjson's wire form is plain JSON shaped as {"json": <value>, "meta": { "values": { "<path>": "<type>" }, ... }}. The Json tree in this library has no Date, BigInt, Map or Set type, so there is nothing here to rebuild a JavaScript value into — and inventing one would not be faithful, because the whole point of the envelope is that those types live in JavaScript, not on the wire. The honest thing to do is therefore the simplest: parse the envelope as an ordinary JSON object (delegating to @moonjson.loads) and keep json and meta verbatim. The typed values survive as the plain JSON they decode to; the type table survives as the preserved meta object.

Full JavaScript-type reconstruction is out of scope for this Json tree. A caller that needs real Date/BigInt/Map/Set objects must rebuild them from the meta on the JavaScript side, where those types exist.

package

ntv

An IETF Internet-Draft that adds an optional name:type tag to a JSON value by wrapping it in an object whose single member carries the tag as its key (e.g. {"paris:point": [2.35, 48.85]}). Because that wrapping is itself valid JSON, this package does not keep a separate tree: it reads and writes the value with the shared Json tree, leaving name:type keys exactly as written. See ntv.mbt for the full design note.

dump

pub fn dump(Json, indent? : Int32, ascii? : Bool, sort? : Bool) -> Bytes

The same, as the UTF-8 bytes a body goes out as.

See dumps for the design note: a tagged value is written back as the JSON object with its name:type key intact.

dumps

pub fn dumps(Json, indent? : Int32, ascii? : Bool, sort? : Bool) -> String

Write a tree as strict JSON, whatever dialect it was read from.

indent of zero is the compact form. ascii escapes everything above U+007E, for a transport that is not eight-bit clean — what is sometimes called ensure_ascii, except that it is off by default here, because a UTF-8 body is the normal case now. sort writes members in order of name instead of the order they were read in, which is what a diff wants.

A JSON-NTV value leaves here still a JSON object with a name:type key, because this delegates to @moonjson.dumps and does not rewrite keys.

load

pub fn load(BytesView, flavor? : @moonjson.Flavor, bom? : Bool) -> Json raise @moonjson.Refused

The same, from the UTF-8 bytes a body arrives as.

See loads for the design note: a JSON-NTV document is valid JSON, so the bytes are read with the shared reader and any name:type key is preserved as a plain object key.

loads

pub fn loads(StringView, flavor? : @moonjson.Flavor) -> Json raise @moonjson.Refused

Read one JSON-NTV document.

DESIGN — JSON-NTV is JSON. A tagged value such as {"paris:point": [2.35, 48.85]} is a perfectly ordinary JSON object with one member whose key happens to read name:type. There is therefore no separate tree to parse into: this call delegates to @moonjson.loads, so the name:type key is kept verbatim as the member key it is.

LIMITATION — because the shared Json tree has no notion of a typed name, the name and type parts of the key are not split apart or interpreted; they survive only as a plain object key. A reader that needs the name or the type must read the key string and divide it on the last :. This is the faithful and honest reading: a tagged value is valid JSON, and nothing here pretends otherwise.

package

rjson

"rjson" is most often a shorthand for the Ruby json standard-library, or the rjson gem built on it. Both serialize Ruby objects to standard JSON and parse standard JSON back; there is no separate "rjson" textual grammar. This package therefore delegates to @moonjson — an rjson payload is exactly JSON.

dump

pub fn dump(Json, indent? : Int32, ascii? : Bool, sort? : Bool) -> Bytes

The same, as the UTF-8 bytes a body goes out as.

Writes the tree as ordinary JSON, delegating to @moonjson.dump; an rjson payload is standard JSON on the wire.

dumps

pub fn dumps(Json, indent? : Int32, ascii? : Bool, sort? : Bool) -> String

Write a tree as standard JSON, whatever dialect it was read from.

indent of zero is the compact form. ascii escapes everything above U+007E, for a transport that is not eight-bit clean. sort writes members in order of name instead of the order they were read in, which is what a diff wants. An rjson payload IS standard JSON, so this is ordinary output.

load

pub fn load(BytesView, flavor? : @moonjson.Flavor, bom? : Bool) -> Json raise @moonjson.Refused

The same, from the UTF-8 bytes a body arrives as.

Reads rjson as ordinary JSON, delegating to @moonjson.load; an rjson payload is standard JSON regardless of how it is transported.

loads

pub fn loads(StringView, flavor? : @moonjson.Flavor) -> Json raise @moonjson.Refused

Read one rjson document.

The Ruby json library emits and accepts standard JSON (RFC 8259), so this delegates to @moonjson.loads; an rjson payload is ordinary JSON.

package

json11

json11 is a tiny C++11 library providing JSON parsing and serialization; its output is standard JSON (RFC 8259) — the library adds no syntax of its own. This package therefore delegates to @moonjson — a json11 payload is exactly JSON.

dump

pub fn dump(Json, indent? : Int32, ascii? : Bool, sort? : Bool) -> Bytes

The same, as the UTF-8 bytes a body goes out as.

Writes the tree as ordinary JSON, delegating to @moonjson.dump; a json11 payload is standard JSON on the wire.

dumps

pub fn dumps(Json, indent? : Int32, ascii? : Bool, sort? : Bool) -> String

Write a tree as standard JSON, whatever dialect it was read from.

indent of zero is the compact form. ascii escapes everything above U+007E, for a transport that is not eight-bit clean. sort writes members in order of name instead of the order they were read in, which is what a diff wants. A json11 payload IS standard JSON, so this is ordinary output.

load

pub fn load(BytesView, flavor? : @moonjson.Flavor, bom? : Bool) -> Json raise @moonjson.Refused

The same, from the UTF-8 bytes a body arrives as.

Reads json11 as ordinary JSON, delegating to @moonjson.load; a json11 payload is standard JSON regardless of how it is transported.

loads

pub fn loads(StringView, flavor? : @moonjson.Flavor) -> Json raise @moonjson.Refused

Read one json11 document.

json11 emits and parses standard JSON, so this delegates to @moonjson.loads; a json11 payload is ordinary JSON.

package

jsonz

JSON-Z is a superset of JSON (and of JSON5) that adds comments, single and backtick quotes, unquoted keys, trailing commas, hex/octal/binary numbers, +-prefixed and Infinity/-Infinity/NaN numbers, and more. This package reads and writes the JSON-representable subset by delegating to @moonjson's relaxed reader; the JSON-Z-exclusive numeric suffixes (BigInt n, decimal m, Decimal128 d), sparse arrays, undefined, and extended typed containers cannot be held by the shared Json tree and are out of scope.

dump

pub fn dump(Json, indent? : Int32, ascii? : Bool, sort? : Bool) -> Bytes

The same, as the UTF-8 bytes a body goes out as.

Writes the tree as ordinary JSON, delegating to @moonjson.dump; a value read from JSON-Z's representable subset is written back as JSON.

dumps

pub fn dumps(Json, indent? : Int32, ascii? : Bool, sort? : Bool) -> String

Write a tree as strict JSON, whatever dialect it was read from.

indent of zero is the compact form. ascii escapes everything above U+007E, for a transport that is not eight-bit clean. sort writes members in order of name instead of the order they were read in, which is what a diff wants. The JSON-Z representable subset round-trips as JSON here; the JSON-Z-exclusive numeric and container extensions cannot be reconstructed.

load

pub fn load(BytesView, flavor? : @moonjson.Flavor, bom? : Bool) -> Json raise @moonjson.Refused

The same, from the UTF-8 bytes a body arrives as.

Reads JSON-Z as ordinary JSON-Z (the JSON5-compatible subset), delegating to @moonjson.load; see loads for the JSON-Z features this package keeps and the ones it cannot represent in a Json tree.

loads

pub fn loads(StringView, flavor? : @moonjson.Flavor) -> Json raise @moonjson.Refused

Read one JSON-Z document.

JSON-Z shares its JSON5-compatible surface — comments, single and backtick quotes, unquoted object keys, trailing commas, hexadecimal and signed numbers, Infinity and NaN — with the relaxed reader, so those are parsed here by delegating to @moonjson.loads with flavor = relaxed.

LIMITATION — JSON-Z adds values the shared Json tree cannot hold: BigInt (the n suffix), arbitrary-precision decimals (m) and IEEE-754 Decimal128 (d), sparse arrays, undefined, and extended typed containers (Date, Map, Set, RegExp, Uint8Array). These are out of scope for a Json tree that has none of those types, exactly as superjson and ntv are out of scope for the types they cannot rebuild. A document that uses them is not accepted here.

package

zson

ZSON is a PostgreSQL extension for transparent JSONB compression. It is a server-side, dictionary-trained compression of JSONB; it is NOT a portable wire format — the stored bytes depend on a per-database trained dictionary, so no standalone decoder is possible. What travels between systems is JSONB, i.e. JSON. This package therefore delegates to the shared JSON reader/writer; see zson.mbt for the full design note. (Do not confuse it with ZJSON, the Zed JSON encoding, which lives in moonbitstack/moonjson/zjson.)

dump

pub fn dump(Json, indent? : Int32, ascii? : Bool, sort? : Bool) -> Bytes

The same, as the UTF-8 bytes a body goes out as.

See dumps for the design note: the output is plain JSON ready to be stored into (or sent to) a ZSON column, which then compresses it with its trained dictionary.

dumps

pub fn dumps(Json, indent? : Int32, ascii? : Bool, sort? : Bool) -> String

Write a tree as strict JSON, whatever dialect it was read from.

indent of zero is the compact form. ascii escapes everything above U+007E, for a transport that is not eight-bit clean — what is sometimes called ensure_ascii, except that it is off by default here, because a UTF-8 body is the normal case now. sort writes members in order of name instead of the order they were read in, which is what a diff wants.

The result is the JSON a ZSON column accepts: the compression step that follows is PostgreSQL's, not this package's.

load

pub fn load(BytesView, flavor? : @moonjson.Flavor, bom? : Bool) -> Json raise @moonjson.Refused

The same, from the UTF-8 bytes a body arrives as.

See loads for the design note: a ZSON value is jsonb — that is, JSON — so the bytes are read with the shared reader and the compression itself is out of scope for a portable library.

loads

pub fn loads(StringView, flavor? : @moonjson.Flavor) -> Json raise @moonjson.Refused

Read one ZSON document.

ZSON is a PostgreSQL extension that compresses jsonb transparently, using a shared dictionary trained on the data a deployment actually stores. That makes it a *server-side* compression, not a portable wire format: the bytes on disk depend on a per-database dictionary that is not part of the document, so no standalone decoder can turn them back into a value. What travels between systems — the jsonb a column holds, the text a client sends or receives — is ordinary JSON, and that is the only part this library can faithfully handle. The honest reading is therefore a delegation: parse the value as JSON with the shared reader. A caller that needs real ZSON storage must use PostgreSQL with the extension installed; this package only understands the JSON a ZSON column carries.

(Do not confuse this with *ZJSON*, the Zed JSON encoding — a different format, implemented in moonbitstack/moonjson/zjson.)

package

zjson

ZJSON is the JSON encoding of the Zed data model: a stream of JSON objects, each of the form {"type": <zed-type>, "value": <json-value>}. It is valid JSON — the Zed type lives in a member alongside the value — so this package delegates to the shared JSON reader/writer and keeps the type/value members verbatim; see zjson.mbt for the full design note. (Do not confuse it with ZSON, the PostgreSQL JSONB-compression extension, which lives in moonbitstack/moonjson/zson.)

dump

pub fn dump(Json, indent? : Int32, ascii? : Bool, sort? : Bool) -> Bytes

The same, as the UTF-8 bytes a body goes out as.

See dumps for the design note: the type/value envelope is written back as the JSON object it is, delegating to @moonjson.dump.

dumps

pub fn dumps(Json, indent? : Int32, ascii? : Bool, sort? : Bool) -> String

Write a tree as strict JSON, whatever dialect it was read from.

indent of zero is the compact form. ascii escapes everything above U+007E, for a transport that is not eight-bit clean — what is sometimes called ensure_ascii, except that it is off by default here, because a UTF-8 body is the normal case now. sort writes members in order of name instead of the order they were read in, which is what a diff wants.

A ZJSON value leaves here still a JSON object carrying type and value, because this delegates to @moonjson.dumps and does not rewrite members.

load

pub fn load(BytesView, flavor? : @moonjson.Flavor, bom? : Bool) -> Json raise @moonjson.Refused

The same, from the UTF-8 bytes a body arrives as.

See loads for the design note: a ZJSON document is valid JSON, so the bytes are read with the shared reader and the type/value members are kept exactly as written.

loads

pub fn loads(StringView, flavor? : @moonjson.Flavor) -> Json raise @moonjson.Refused

Read one ZJSON document.

ZJSON is the JSON encoding of the Zed data model: a stream of JSON objects, each of the form {"type": <zed-type>, "value": <json-value>}, where type describes the rich Zed type (a primitive name like "int64", or a complex type built from record/array/map/union/enum/error/ref nodes) and value is the value rendered in plain JSON. It is, underneath, valid JSON — the type lives in a member alongside the value.

That is the same situation as superjson's envelope: the Json tree in this library has no Zed type system to rebuild into, so the faithful, honest reading is a delegation. This call parses the document as ordinary JSON with the shared reader, keeping type and value verbatim. A caller that needs real Zed typed values must interpret type against the Zed type model in a Zed-aware client — there is nothing here to lose by reading it as JSON, only the type annotation, which survives as the preserved type member.

(Do not confuse this with *ZSON*, the PostgreSQL JSONB-compression extension — a different format, implemented in moonbitstack/moonjson/zson.)

package

cson

A line- and indentation-based dialect of JSON: no braces, no commas, keys and values separated by colons and newlines, nesting shown by indentation, arrays by - bullets. The reader parses it into the shared Json tree; the writer delegates to @moonjson, because CSON has no canonical text form and what it does have does not survive a round trip, so the only faithful rendering is the strict JSON @moonjson already knows how to write.

dump

pub fn dump(Json, indent? : Int32, ascii? : Bool, sort? : Bool) -> Bytes

The same, as the UTF-8 bytes a body goes out as.

Like dumps, the text produced is strict JSON written by @moonjson; CSON itself has no faithful textual rendering.

dumps

pub fn dumps(Json, indent? : Int32, ascii? : Bool, sort? : Bool) -> String

Write a tree as strict JSON.

CSON has no canonical text form — nothing the writer could print would be CSON a reader recognises as the same document — so this delegates to @moonjson.dumps, which already knows how to emit strict JSON for any Json.

load

pub fn load(BytesView, flavor? : @moonjson.Flavor, bom? : Bool) -> Json raise @moonjson.Refused

Read one CSON document from the UTF-8 bytes a body arrives as.

A leading byte-order mark is skipped by default. If the bytes are not valid UTF-8 the document is refused rather than silently mangled.

loads

pub fn loads(StringView, flavor? : @moonjson.Flavor) -> Json raise @moonjson.Refused

Read one CSON document.

The root is an object whose members sit at the first indentation level (or an array, if the first line is a - bullet). CSON is indentation-based, so the document is first reduced to structural lines and then walked recursively; what comes back is the same Json tree every dialect in this family reads into.

package

hjson

Hjson — a user interface for JSON: https://hjson.github.io/

dump

pub fn dump(Json, indent? : Int32, ascii? : Bool, sort? : Bool) -> Bytes

The same, as the UTF-8 bytes a body goes out as.

dumps

pub fn dumps(Json, indent? : Int32, ascii? : Bool, sort? : Bool) -> String

Write a tree as strict JSON, whatever dialect it was read from.

Hjson has no canonical text form of its own — the same tree can be written many ways — so the writer emits strict JSON by delegating to @moonjson.

load

pub fn load(BytesView, flavor? : @moonjson.Flavor, bom? : Bool) -> Json raise @moonjson.Refused

Read one Hjson document from the UTF-8 bytes a body arrives as.

A leading byte-order mark is skipped by default. Decoding that fails is a Bad refusal.

loads

pub fn loads(StringView, flavor? : @moonjson.Flavor) -> Json raise @moonjson.Refused

Read one Hjson document from text.

Hjson is a superset of JSON as far as the tree is concerned, so whatever comes back here can be written out again as strict JSON with dumps. The flavor argument is accepted for the family's common signature; Hjson is always read leniently.

package

sink

Streaming JSON sink — newline-delimited JSON (NDJSON): https://github.com/ndjson/ndjson-spec

Sink

pub struct Sink

A sink that accumulates a sequence of JSON documents, one to a line.

The documents are buffered in memory; the value of the type is that it is written one document at a time, so a caller that produces them lazily never builds the whole array first.

Fields
  • buffer : StringBuilder

    The bytes written so far.

Methods
  • pub fn Sink::length(Self) -> Int32

    How many documents have been written.

  • pub fn Sink::new() -> Self

    A new, empty sink.

  • pub fn Sink::to_bytes(Self) -> Bytes

    Everything written so far, as the UTF-8 bytes a body goes out as.

  • pub fn Sink::to_string(Self) -> String

    Everything written so far, as text.

  • pub fn Sink::write(Self, Json) -> Unit

    Write one document, in the compact form, followed by a line feed.

    Every record ends in its own line feed, including the last, which is what makes the output appendable — the format's whole purpose.

package

source

Streaming JSON source — newline-delimited JSON (NDJSON): https://github.com/ndjson/ndjson-spec

Source

pub struct Source

A source that hands back a sequence of JSON documents, one at a time.

Fields
  • text : StringView

    The stream as decoded text.

Methods
  • pub fn Source::is_done(Self) -> Bool

    Whether the stream has been fully read.

  • pub fn Source::length(Self) -> Int32

    How many documents have been read so far.

  • pub fn Source::new(BytesView) -> Self raise @moonjson.Refused

    A source over the UTF-8 bytes of a stream.

    A leading byte-order mark is skipped. Bytes that are not UTF-8 are a BadUtf8 refusal at the start of the stream.

  • pub fn Source::read(Self) -> Json? raise @moonjson.Refused

    The next document, or None once the stream is exhausted.

    A blank line is skipped. A line that is not a value is refused with the position of the failure in that line.

package

pretty

The core writer already writes an indented form; this package adds the knobs a formatter wants — tabs instead of spaces, CRLF line endings — and the text-to-text entry point a command-line tool needs, which is what python -m json.tool does: read a document, write it back in the chosen shape. It is a package of its own rather than part of the core because the core's dumps signature is depended on everywhere and must stay stable, and because a build that never imports this one never compiles it.

format

pub fn format(Json, style? : Format) -> String

Write a tree in the chosen shape.

The core writer does the work; this only bends its output where the core's signature cannot — a tab for each level, a carriage return before each line feed. A JSON string never contains a raw line feed (it is escaped), so rewriting line endings cannot touch a value.

pretty

pub let pretty : Format

The default: two spaces a level, LF line endings, the order the tree keeps.

reformat

pub fn reformat(StringView, style? : Format) -> String raise @moonjson.Refused

Read a document and write it back in the chosen shape — the text-to-text entry point, what python -m json.tool does.

reformat_bytes

pub fn reformat_bytes(BytesView, style? : Format) -> Bytes raise @moonjson.Refused

The same, from the UTF-8 bytes of a document.

Format

pub(all) struct Format

How to lay a document out.

The defaults are the ones a formatter is usually asked for — two spaces a level, LF line endings — and every field can be changed independently.

Fields
  • indent : Int32

    Spaces per level. Zero writes the whole document on one line.

  • tabs : Bool

    One tab per level instead of spaces; indent is then ignored.

  • crlf : Bool

    A carriage return before each line feed, for a file Windows reads.

  • sort : Bool

    Sort members by name, code point by code point.

  • ascii : Bool

    Escape everything above U+007F so the output is plain ASCII.

Methods
  • pub fn Format::equal(Self, Self) -> Bool

    Written by derive(Eq).

  • pub fn Format::new(indent? : Int32, tabs? : Bool, crlf? : Bool, sort? : Bool, ascii? : Bool) -> Self

    Build a format by naming the parts that differ from the default.

  • pub fn Format::not_equal(Self, Self) -> Bool

    Written by derive(Eq).

  • pub fn Format::to_repr(Self) -> @debug.Repr

    Written by derive(Debug).