The error's path in jsonpath form, as jsonb_path_query takes it: $ for the document itself,
$.address.tags[2], and a key that is not a plain identifier quoted: $."we ird". Allocated
with the temporary allocator.
#import "Extensions/Json";
Typed JSON for jaic programs, in the spirit of Rust's serde_json. Encoders and decoders are generated from your types at compile time, so there is no reflection walk at run time. A jaic extension, not official Jai: other Jai compilers have no such module, and it may change between jaic releases. See docs/extensions/json.md.
#import "Extensions/Json";
User :: struct @json(rename_all=camelCase) {
id: int;
display_name: string; // "displayName"
email: string; @json(skip_serializing_if=empty)
tags: [..] string;
}
ada := User.{id = 7, display_name = "Ada"};
text := to_json(ada); // pretty: to_json(ada, pretty = true)
user, error := from_json(User, text); // error.kind == .NONE on success
value, value_error := from_json(Json_Value, text); // when the shape is unknown
free_json(*user); // also for a Json_Value and a Json_ErrorProcedures#
General#
message at line 3, column 5 (tags[2])
Releases the message and path of an error and clears it. Part of the free_json set.
Encodes value. Output is compact unless pretty (two-space indent). The result comes from
context.allocator.
Decodes a T from a source in pieces. The pieces are gathered in one buffer (the decoder wants
the whole document, and its strings are copied out of it, so the buffer is freed before this
returns) and then decoded exactly as from_json(T, text) would, so a document split at any
byte gives the same value or the same error. A source that fails gives a .READ error with
the source's text; a document longer than options.max_size is a .LIMIT error and the rest
is not read.
Writes the JSON of the value value points to. This and json_decode are what with=
handlers call for the parts they do not encode themselves.
Reads a T into out; false after recording an error.
Value#
NaN and infinities have no JSON spelling and become null.
The string is copied.
Appends to an array; array takes ownership of item.
Sets a member of an object, replacing the last member with that key; object takes
ownership of value and copies key.
The member key of an object (the last one, if the key repeats), or null.
Element index of an array, or null.
How many elements an array has, or members an object has; 0 for anything else.
A number that is written as an integer and fits. False for anything else.
Any number, rounded to the nearest float64. False if it is not a number or is too large.
Writer#
Appends text as a JSON string with its quotes. Control characters, quotes and backslashes
are escaped; bytes that are not valid UTF-8 become U+FFFD so the output is always valid JSON.
Appends text that is already JSON, as is.
{. Pair it with json_end_object and put a json_key before each member.
Starts a member: the separator, then the key. quoted_key is the key with its quotes and
escapes already applied (the generated code does that at compile time).
json_key with the compact forms prebuilt at compile time: first_form is "key": and
next_form is ,"key":, so compact output costs one append.
A member whose key is only known at run time (a Hash_Table's).
Starts an array element.
Reader#
Records an error at the current position (when there is none yet) and returns false.
Say the value being read is at key / index of its parent. The generated decoders call
these while an error travels up, so the error's path reads address.tags[2].
The next significant byte (0 at the end of the input).
Reads up to the next member of the object: its key (valid until the next read) and the colon
after it, or the closing brace. first is true before the first call.
A string value, copied into new memory from context.allocator (with borrow, a slice of the
input when it has no escapes).
A string value without copying: a view of the input, or of the reader's scratch space when it has escapes. Valid until the next read; copy it to keep it.
Keeps a key from json_read_name: a slice of the input with borrow (unless it had escapes and
lives in the scratch space), else a copy.
Reads an integer into out, which must fit between low and high. type_name appears in
the message when it does not.
Checks the next value and moves past it without keeping anything.
Reads any value into a Json_Value. On failure out holds what was read so far, and
free_json releases it.
Query#
Decodes a T from pairs. A key that repeats gives an array for an array field, and is a
"duplicate field" error for any other. Keys no field has are ignored unless T is marked
deny_unknown_fields. Everything allocated (strings, arrays) comes from context.allocator and
is released by free_json, as for from_json.
Search :: struct @json(default) { q: string; limit := 20; tags: [..] string; }
// The pairs of "?q=jai&tags=a&tags=b".
pairs := Query_Pair.[.{"q", "jai"}, .{"tags", "a"}, .{"tags", "b"}];
search, error := from_query(Search, pairs);
assert(error.kind == .NONE);
assert(search.q == "jai");
assert(search.limit == 20);
assert(search.tags.count == 2);↗Decodes a T from one piece of text: an integer, float, bool, string or enum. from_param(int, "42") is 42; from_param(Color, "red") the member red; a string is taken as it is.
Types#
Why decoding failed and where. path names the value inside the document (tags[2],
address.zip); line and column count from 1, the column in characters. message and
path are allocated with context.allocator: free_json releases them.
A field that distinguishes a key that is missing, a null and a value. As a struct member a
missing key leaves .MISSING (never a missing-field error), null gives .NULL and any other
value decodes into value and gives .SET. Encoding omits the key for .MISSING, writes
null for .NULL and the value for .SET. Outside a struct member (the document, an array
element) .MISSING encodes as null. free_json releases value when it is .SET.
How the structural index fared since the program began, for tests and curiosity.
Where a document comes from when it is not in memory yet: fill puts the next piece of text in
buffer and returns how many bytes it wrote (0 at the end), and a non-empty string when the
source failed (what went wrong). data is handed to it as it is. Json does not know what the
pieces come from; Io.read_json makes one from an Io.Reader.
One JSON value. Only the fields of its kind are used: BOOL boolean NUMBER text, the number as written (exact for integers of any size; see json_as_s64) STRING text, the contents with escapes resolved ARRAY items OBJECT members, in document order (duplicate keys are kept; json_get finds the last) Everything a Json_Value owns comes from context.allocator; free_json releases it.
Reads one JSON document. Every reading procedure returns false after recording an error in
error; the first error wins, and callers must stop at once and return false themselves.
One key=value of a query or a form, already percent-decoded.