std/json
JSON values are a Json enum with a member for each kind of value:
enum Json do
NULL
BOOL(bool value)
NUMBER(f64 value)
STRING(str value)
ARRAY(Json[] items)
OBJECT(map[str, Json] fields)
endA match takes any value apart; the functions below cover the usual
lookups. Objects keep their fields in the order they were read or set.
import "std/json"
match parse_json(read_text("config.json")) do
when OK(config) then
print(json_text(json_get(config, "name")))
print(json_number(json_get(config, "port"), 8080.0))
when ERROR(message) then
eprint("config.json: " + message)
end| Function | Result |
|---|---|
parse_json(text) |
a Result[Json]; an error says where, as in line 2, column 7: expected ',' or '}' |
to_json(value) |
compact JSON text on one line |
pretty_json(value) |
JSON text with one item or field per line, indented by two spaces |
json_get(value, key) |
field key of an object, as an Option[Json] |
json_item(value, index) |
item index of an array, as an Option[Json] |
json_len(value) |
the number of items or fields; 0 for other values |
json_text(found) |
the text of a STRING in an Option[Json], or "" |
json_number(found, fallback) |
the number of a NUMBER, or fallback |
json_bool(found, fallback) |
the value of a BOOL, or fallback |
json_items(found) |
the items of an ARRAY, or none, for a for loop |
json_object(), json_array() |
a new empty object or array |
json_set(object, key, value) |
sets a field of an object |
json_push(array, value) |
adds an item at the end of an array |
The code that #[derive(Json)] writes converts values with these, which
programs can use too:
| Function | Result |
|---|---|
json_of_str(text) |
a STRING, or NULL for a null str |
json_of_array(items, convert), json_of_map(fields, convert), json_of_option(value, convert) |
an ARRAY, OBJECT, or the value, with each item converted, NULL for null or NONE |
json_to_str(value), json_to_f64(value), json_to_bool(value) |
the value as a Result, an error for another kind of value |
json_to_array(value, convert), json_to_map(value, convert), json_to_option(value, convert) |
the items converted, as a Result whose error says which item failed |
json_is_object(value) |
whether it is an OBJECT |
json_string(value) |
the text of a STRING, as an Option[str] |
The lookups take an Option[Json], so they chain: json_text(json_get(user, "name")) is "" when user has no name or is not an object. Numbers are
written with the fewest digits that read back as the same f64, such as
0.1, 31, and 6.02e23. JSON has no NaN or infinity; they are written as
null. Text cannot hold the character \u0000, so reading it is an error,
as is nesting arrays and objects more than 500 deep.
parse_json(text, jsonc: true) reads JSONC, as editors' settings files
have it: comments, // to the end of the line and /* ... */, and a comma
after the last item or field.
| Function | Result |
|---|---|
json_equal(a, b) |
whether two values are the same JSON: numbers by value, objects whatever the order of their fields |
json_copy(value) |
a copy that shares no array or object with the value |
parse_json_lines(text), to_json_lines(values) |
JSON Lines: one value a line, blank lines left out |
json_at(document, pointer) |
the value a JSON Pointer (RFC 6901) points to, as "/items/0/name" or "#/items/0", as an Option[Json] |
json_pointer(names), json_pointer_names(pointer) |
a pointer from its names, with ~ and / escaped, and back |
json_patch(document, patch) |
the document with a JSON Patch (RFC 6902) applied, as a Result[Json] |
json_merge_patch(target, patch) |
the target with a JSON Merge Patch (RFC 7386) applied |
A patch's operations, add, remove, replace, move, copy, and
test, are applied in order to a copy, so the document given is never
changed, and an ERROR names the operation that failed: of kind
"test_failed" for a test that does not hold, "invalid_patch" for the
rest.
Reading and writing in parts
A document too large to hold, or one that arrives a part at a time, is read
as a stream of events from a Reader, such as a file, and written to a
Writer a part at a time:
import "std/json"
import "std/fs"
// The items of a large array, one at a time.
JsonStream s = json_stream_from(open_file("orders.json")?)
next_json_event(s)? // START_ARRAY
while true do
match next_json(s)? do
when SOME(order) then
print(json_text(json_get(order, "id")))
when NONE then
break // the end of the array
end
end| Function | Result |
|---|---|
json_stream(text), json_stream_from(source) |
a JsonStream over text, or over a Reader, which it reads as it needs more |
next_json_event(s) |
the next JsonEvent: START_OBJECT, END_OBJECT, START_ARRAY, END_ARRAY, KEY(name), VALUE(value) for any other value, or END |
next_json(s) |
the whole next value where the stream is: the next item of an array, a field's value after its KEY, or the next value at the top; NONE at the end of the array or of the text |
json_writer(out), json_writer(out, pretty: true) |
a JsonWriter to a Writer |
write_json_start_object(w), write_json_end_object(w), write_json_start_array(w), write_json_end_array(w) |
starts or ends an object or array |
write_json_key(w, name) |
the name of the next field of an object |
write_json_value(w, value) |
a whole value, as an item, a field's value, or at the top |
finish_json(w) |
writes what is left and gives the bytes written, or an ERROR for a writer used out of order or left with something open |
A stream takes sequence: true for several values one after another, as
JSON Lines has them, and then reads values until the text ends; a writer
puts each value at the top on a line of its own, so given several it
writes JSON Lines. A stream also takes jsonc: true. An ERROR while reading says where, as parse_json does,
and the stream then gives it on every call; the text must be UTF-8.