Module

pooka/json

src/pooka/json.pooka

Read and write JSON, with Erlang's own json module doing the heavy lifting.

A JSON document becomes a Json value: one of seven small classes, one for each kind of thing JSON can say. Take it apart with match, or with the helpers at the end of this module.

Pooka
import pooka/json

main() {
  let assert Ok(owl) = json.decode("{\"name\": \"Ada\", \"age\": 3}")
  echo json.get(owl, "name")
  echo json.encode(json.JArray([json.JInt(1), json.JNull()]))
}

Objects keep their entries in the order they were written, so a document that is decoded and encoded again comes back in the same order.

API reference

class

Json

src/pooka/json.pooka:29
class Json

Any JSON value.

class

JNull

src/pooka/json.pooka:32
class JNull is Json

null

class

JBool

src/pooka/json.pooka:35
class JBool(value: Bool) is Json

true or false

field

value

src/pooka/json.pooka:35
value: Bool
INFERREDBool
constructor

new

src/pooka/json.pooka:35
INFERREDfn(Bool) -> pooka/json.JBool
class

JInt

src/pooka/json.pooka:38
class JInt(value: Int) is Json

A number written without a fraction or exponent, such as 42.

field

value

src/pooka/json.pooka:38
value: Int
INFERREDInt
constructor

new

src/pooka/json.pooka:38
INFERREDfn(Int) -> pooka/json.JInt
class

JFloat

src/pooka/json.pooka:41
class JFloat(value: Float) is Json

A number written with a fraction or exponent, such as 2.5 or 1e3.

field

value

src/pooka/json.pooka:41
value: Float
INFERREDFloat
constructor

new

src/pooka/json.pooka:41
INFERREDfn(Float) -> pooka/json.JFloat
class

JString

src/pooka/json.pooka:44
class JString(value: String) is Json

A string, with its escapes already resolved.

field

value

src/pooka/json.pooka:44
value: String
INFERREDString
constructor

new

src/pooka/json.pooka:44
INFERREDfn(String) -> pooka/json.JString
class

JArray

src/pooka/json.pooka:47
class JArray(items: List(Json)) is Json

An array.

field

items

src/pooka/json.pooka:47
items: List(Json)
INFERREDList(pooka/json.Json)
constructor

new

src/pooka/json.pooka:47
INFERREDfn(List(pooka/json.Json)) -> pooka/json.JArray
class

JObject

src/pooka/json.pooka:51
class JObject(entries: List(#(String, Json))) is Json

An object, as its entries in the order they were written. A key that appears twice is kept twice; get returns the first.

field

entries

src/pooka/json.pooka:51
entries: List(#(String, Json))
INFERREDList(#(String, pooka/json.Json))
constructor

new

src/pooka/json.pooka:51
INFERREDfn(List(#(String, pooka/json.Json))) -> pooka/json.JObject
interface

Encodable

src/pooka/json.pooka:68
interface Encodable

A promise an application value can make without joining Json's closed family. The class chooses its JSON shape; the ordinary writers handle the text.

Pooka
import pooka/json
import pooka/json.{Encodable}

class Bird(name: String) with Encodable {
  to_json() -> json.Json {
    json.JObject([#("name", json.JString(name))])
  }
}

assert json.encode_value(Bird("Ada")) == "{\"name\":\"Ada\"}"
method

to_json

src/pooka/json.pooka:70
to_json() -> Json

Return the JSON representation. The writer calls this method once.

class

JsonError

src/pooka/json.pooka:74
class JsonError

Why a text is not JSON.

class

UnexpectedEnd

src/pooka/json.pooka:77
class UnexpectedEnd is JsonError

The text stopped in the middle of a value.

class

InvalidByte

src/pooka/json.pooka:80
class InvalidByte(byte: Int) is JsonError

A byte that cannot appear where it was found, as its numeric value.

field

byte

src/pooka/json.pooka:80
byte: Int
INFERREDInt
constructor

new

src/pooka/json.pooka:80
INFERREDfn(Int) -> pooka/json.InvalidByte
class

UnexpectedSequence

src/pooka/json.pooka:83
class UnexpectedSequence(text: String) is JsonError

A broken escape or invalid UTF-8 inside a string.

field

text

src/pooka/json.pooka:83
text: String
INFERREDString
constructor

new

src/pooka/json.pooka:83
INFERREDfn(String) -> pooka/json.UnexpectedSequence
class

NumberOutOfRange

src/pooka/json.pooka:86
class NumberOutOfRange(text: String) is JsonError

A syntactically valid number whose magnitude is outside the BEAM Float range.

field

text

src/pooka/json.pooka:86
text: String
INFERREDString
constructor

new

src/pooka/json.pooka:86
INFERREDfn(String) -> pooka/json.NumberOutOfRange
class

LimitExceeded

src/pooka/json.pooka:89
class LimitExceeded(limit: String) is JsonError

An input byte or nesting limit was exceeded before building the value tree.

field

limit

src/pooka/json.pooka:89
limit: String
INFERREDString
constructor

new

src/pooka/json.pooka:89
INFERREDfn(String) -> pooka/json.LimitExceeded
function

decode

src/pooka/json.pooka:114
decode(text: String) -> Result(Json, JsonError)
INFERREDfn(String) -> Result(pooka/json.Json, pooka/json.JsonError)

Read one JSON document.

Pooka
import pooka/json

assert json.decode("[1, true]") == Ok(json.JArray([json.JInt(1), json.JBool(True)]))
assert json.decode("[1,") == Error(json.UnexpectedEnd())
function

describe

src/pooka/json.pooka:125
describe(problem: JsonError) -> String
INFERREDfn(pooka/json.JsonError) -> String

A sentence for the person who wrote the text.

Pooka
import pooka/json

assert json.describe(json.UnexpectedEnd()) == "the JSON text ended too early"
function

encode

src/pooka/json.pooka:143
encode(value: Json) -> String
INFERREDfn(pooka/json.Json) -> String

Write a value as compact JSON, with no spaces.

Pooka
import pooka/json

let owl = json.JObject([#("name", json.JString("Ada")), #("age", json.JInt(3))])
assert json.encode(owl) == "{\"name\":\"Ada\",\"age\":3}"
function

encode_pretty

src/pooka/json.pooka:154
encode_pretty(value: Json) -> String
INFERREDfn(pooka/json.Json) -> String

Write a value as JSON for people, indented by two spaces.

Pooka
import pooka/json

assert json.encode_pretty(json.JArray([json.JInt(1), json.JInt(2)])) == "[\n  1,\n  2\n]"
function

encode_value

src/pooka/json.pooka:165
encode_value(value: Encodable) -> String
INFERREDfn(pooka/json.Encodable) -> String

Write an application value through its Encodable promise, as compact JSON. Existing Json values continue to use encode directly.

Pooka
// With the Bird class from Encodable's example:
assert json.encode_value(Bird("Ada")) == "{\"name\":\"Ada\"}"
function

encode_value_pretty

src/pooka/json.pooka:175
encode_value_pretty(value: Encodable) -> String
INFERREDfn(pooka/json.Encodable) -> String

Write an application value through Encodable, indented by two spaces.

Pooka
// With the Bird class from Encodable's example:
assert json.encode_value_pretty(Bird("Ada")) == "{\n  \"name\": \"Ada\"\n}"
function

get

src/pooka/json.pooka:190
get(value: Json, key: String) -> Option(Json)
INFERREDfn(pooka/json.Json, String) -> Option(pooka/json.Json)

The value under a key, if this is an object that has it.

Pooka
import pooka/json

let assert Ok(owl) = json.decode("{\"name\": \"Ada\"}")
assert json.get(owl, "name") == Some(json.JString("Ada"))
assert json.get(owl, "wings") == None
function

path

src/pooka/json.pooka:205
path(value: Json, keys: List(String)) -> Option(Json)
INFERREDfn(pooka/json.Json, List(String)) -> Option(pooka/json.Json)

Follow several keys, one object inside another.

Pooka
import pooka/json

let assert Ok(nest) = json.decode("{\"owl\": {\"name\": \"Ada\"}}")
assert json.path(nest, ["owl", "name"]) == Some(json.JString("Ada"))
function

at

src/pooka/json.pooka:223
at(value: Json, index: Int) -> Option(Json)
INFERREDfn(pooka/json.Json, Int) -> Option(pooka/json.Json)

The item at a position, counting from zero, if this is an array that has it.

Pooka
import pooka/json

let assert Ok(row) = json.decode("[10, 20]")
assert json.at(row, 1) == Some(json.JInt(20))
function

as_string

src/pooka/json.pooka:231
as_string(value: Json) -> Option(String)
INFERREDfn(pooka/json.Json) -> Option(String)

The text, if this is a string.

function

as_int

src/pooka/json.pooka:239
as_int(value: Json) -> Option(Int)
INFERREDfn(pooka/json.Json) -> Option(Int)

The number, if this is a whole number.

function

as_float

src/pooka/json.pooka:248
as_float(value: Json) -> Option(Float)
INFERREDfn(pooka/json.Json) -> Option(Float)

The number as a Float. JSON does not tell 3 from 3.0, so both are accepted here.

function

as_bool

src/pooka/json.pooka:260
as_bool(value: Json) -> Option(Bool)
INFERREDfn(pooka/json.Json) -> Option(Bool)

The truth value, if this is true or false.

function

as_list

src/pooka/json.pooka:268
as_list(value: Json) -> Option(List(Json))
INFERREDfn(pooka/json.Json) -> Option(List(pooka/json.Json))

The items, if this is an array.

function

as_entries

src/pooka/json.pooka:276
as_entries(value: Json) -> Option(List(#(String, Json)))
INFERREDfn(pooka/json.Json) -> Option(List(#(String, pooka/json.Json)))

The entries, if this is an object.

function

is_null

src/pooka/json.pooka:284
is_null(value: Json) -> Bool
INFERREDfn(pooka/json.Json) -> Bool

Is this null?

function

decode_with_limits

src/pooka/json.pooka:396
decode_with_limits(text: String, max_bytes: Int, max_depth: Int) -> Result(
  Json,
  JsonError
)
INFERREDfn(String, Int, Int) -> Result(pooka/json.Json, pooka/json.JsonError)

Parse untrusted input with explicit byte and container-depth budgets. A scalar has depth zero. A negative budget rejects every input.

Pooka
assert json.decode_with_limits("[[1]]", 100, 1) == Error(json.LimitExceeded("depth"))