Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

json and http

json.parse

json.parse(s) parses JSON: an object, an array or a primitive. Objects become objects, null becomes nil, and a number without a fraction or an exponent is an integer. Malformed input is a ValueError.

JSON is full of " and {, so write it in a raw string, r"""...""".

let user = json.parse(r"""{"name": "ann", "tags": ["admin"], "age": 31, "manager": null}""");
print(user);            // {name: "ann", tags: ["admin"], age: 31, manager: nil}
print(user.tags[0]);    // admin
print(user.manager);    // nil
print(json.parse("[1, 2.5]"));  // [1, 2.5]
print(json.parse("42"));        // 42

Calling a struct type checks the shape of what was parsed:

struct Order { id, total, items = [] }

let order = Order(json.parse(r"""{"id": 8, "total": 5}"""));
print(order);   // Order {id: 8, total: 5, items: []}
json.parse("not json"); // ValueError: json.parse() could not parse JSON: invalid character 'o' in literal null (expecting 'u')

json.stringify

json.stringify(v[, indent]) encodes nil, booleans, numbers, strings, arrays, objects (keys in order), structs (fields in order), sets (as arrays) and maps of string keys (as objects, in order), compactly unless indent, a number of spaces or a string of spaces and tabs, at most 10, puts each element on its own line. A function is a TypeError, a non-finite number or a cycle a ValueError.

struct Order { id, total, items = [] }

print(json.stringify({ name: "ann", active: true, manager: nil }));
// {"name":"ann","active":true,"manager":null}
print(json.stringify(Order { id: 7, total: 19.99 }));
// {"id":7,"total":19.99,"items":[]}
print(json.stringify(set(["a", "b"])));   // ["a","b"]
print(json.stringify("say \"hi\""));      // "say \"hi\""
print(json.stringify({ name: "ann", tags: ["admin"] }, 2));
// {
//   "name": "ann",
//   "tags": [
//     "admin"
//   ]
// }
json.stringify({ onClick: () => nil }); // TypeError: json.stringify() cannot encode function.

http.get

http.get(url) fetches a URL and returns the body as a string; a network error or a status other than 200 is an IOError.

let todos = json.parse(http.get("https://api.example.com/todos")).todos;
print(todos.filter(t => !t.completed).map(t => t.todo));   // ["buy milk"]
try {
    http.get("https://api.example.com/missing");
} catch e is IOError {
    print(e.message);   // http.get() could not fetch https://api.example.com/missing: status code 404
}

http.request

http.request(method, url[, opts]) sends a request and returns HttpResponse { status, headers, body }, header names lowercased and repeated ones joined with , . Any status is a response; a network error or a timeout is an IOError.

opts is an HttpOptions { headers, body, timeout, stream }, or an object converted to one: headers an object of strings, body a string or bytes, timeout seconds (default 30).

let r = http.request("POST", "https://api.example.com/users", {
    headers: { "Content-Type": "application/json" },
    body: json.stringify({ name: "ann" }),
    timeout: 5,
});

print(r.status);                  // 201
print(r.headers["location"]);     // /users/42
print(json.parse(r.body).id);     // 42

if r.status != 201 throw IOError { message: "status {r.status}" };
let r = http.request("GET", "https://api.example.com/missing");
print(r.status);   // 404
print(json.parse(r.body).error);   // not found

With stream: true, body is a Stream to read and close, and the timeout covers only getting the response, so a large download is never held whole:

let r = http.request("GET", "https://api.example.com/export.csv", { stream: true });
for i, line in r.body {
    if i == 0 continue;
    let [id, total] = line.split(",");
    print("order {id}: {total}");
}
r.body.close();
// order 1: 30
// order 2: 120