Standard library Β· Data formats
data/json
Imported as import "data/json" as json, its names are then json.β¦. Every signature below is the one the checker infers.
JSON, written in Rill: a recursive-descent parser and a printer.
Parsing functions return a Parsed β the value together with where it stopped, or why it stopped β because that is how a recursive-descent parser threads position through immutable data. A failure is a Parsed like any other and rides out through the same returns a value does; parse is the only one that turns it into a Result, because the caller is the only one with anywhere else to put it.
doc = json.parse("{\"user\": {\"name\": \"ada\", \"ids\": [1, 2]}}")? json.path(doc, Cons("user", Cons("name", Nil))) # => Some(json.JStr(ada)) json.as_str(json.path(doc, Cons("user", Cons("name", Nil))) |> unwrap_or(json.JNull)) # => ada json.to_string(json.JObj(Cons(json.Field("n", json.JInt(5)), Nil))) # => {"n":5}
Types
Json
JNullJBool(Bool)JInt(Int)JNum(Float)JStr(Str)JArr(List(Json))JObj(List(Field))
Field
Field(key: Str, value: Json)
Parsed
Parsed(value: Json, pos: Int)Failed(why: Str, pos: Int)
Functions
fn parse(text: Str) -> Result(Json, Str)
A document is one value and then nothing but space. Text after that value means the document was not what it claimed to be, and a parser that returns the value anyway has answered a question nobody asked: nonsense is not the JSON document null with a remark after it.
json.parse("[1, 2.5, \"x\", true, null]") # => Ok(json.JArr(Cons(json.JInt(1), Cons(json.JNum(2.5), Cons(json.JStr(x), Cons(json.JBool(true), Cons(json.JNull, Nil))))))) json.parse("[1,]") # => Err(no value begins with `]`, at byte 3) json.parse("null nonsense") # => Err(trailing text, at byte 5)
fn number_of(text: Str) -> Json
JSON has a single number type, but a document id or a count is exact and a measurement is not. A literal that never showed a decimal point or an exponent stays an integer, so it prints back exactly the way it arrived β %g would turn an id of 1234567 into 1.23457e+06.
json.number_of("1234567") # => json.JInt(1234567) json.number_of("1.5e3") # => json.JNum(1500)
fn is_number_char(c: Int) -> Bool
json.is_number_char("7"[0]) # => true json.is_number_char("e"[0]) # => true json.is_number_char(","[0]) # => false
fn to_string(j: Json) -> Str
Written into one builder rather than assembled out of pieces: the old printer built a string per value and a list of them per array, then joined those β so a document of six hundred records made thousands of strings on the way to one. Nothing here makes a string but the builder.
json.to_string(json.JArr(Cons(json.JInt(1), Cons(json.JStr("a\"b"), Nil)))) # => [1,"a\"b"] json.to_string(json.JObj(Nil)) # => {}
fn get(j: Json, key: Str) -> Option(Json)
doc = json.parse("{\"a\": 1}")? json.get(doc, "a") # => Some(json.JInt(1)) json.get(doc, "b") # => None
fn items(j: Json) -> List(Json)
json.items(json.parse("[1, 2]")?) # => Cons(json.JInt(1), Cons(json.JInt(2), Nil)) json.items(json.JNull) # => Nil
fn as_str(j: Json) -> Str
json.as_str(json.JStr("x")) # => x json.as_str(json.JInt(1)) # =>
fn as_float(j: Json) -> Float
json.as_float(json.JNum(2.5)) # => 2.5 json.as_float(json.JInt(2)) # => 2
fn as_int(j: Json) -> Int
For values that arrived as integers β ids, counts, versions. A JNum has no exact integer to give back, so it answers 0 rather than rounding silently.
json.as_int(json.JInt(7)) # => 7 json.as_int(json.JNum(7.9)) # => 0
fn as_bool(j: Json) -> Bool
json.as_bool(json.JBool(true)) # => true json.as_bool(json.JNull) # => false
fn path(j: Json, keys: List(Str)) -> Option(Json)
Walks a path of object keys: path(doc, Cons("user", Cons("name", Nil))).
doc = json.parse("{\"user\": {\"name\": \"ada\"}}")? json.path(doc, Cons("user", Cons("name", Nil))) # => Some(json.JStr(ada)) json.path(doc, Cons("user", Cons("age", Nil))) # => None
fn parsed_or(text: Str, fallback: Json) -> Json
For a caller that has decided a bad document is simply an absent one β a cache entry, an optional config. Anything that answers a user wants the Err and the byte it names.
json.parsed_or("{\"a\": 1}", json.JNull) # => json.JObj(Cons(json.Field(a, json.JInt(1)), Nil)) json.parsed_or("not json", json.JNull) # => json.JNull