radlang — JSON (json)
This document covers the json namespace: parsing, serializing, and navigating
JSON. It supports both a typed style (parse straight into radlang structs)
and a manual style (walk a JsonNode tree by hand).
For language fundamentals see the Overview.
Overview
json is a compiler-intrinsic namespace. The name is reserved and
dispatched directly to the runtime — it needs no import:
type Person {
string name,
int age
}
fn main() {
Person p = Person("Ada", 36)
string encoded = json.from(p) // {"name":"Ada","age":36}
Person back = json.parse<Person>(encoded)
sys.output(back.name) // Ada
} Two built-in types appear here:
JsonNode— an opaque handle to a parsed JSON value (object, array, string, number, bool, or null). You never construct it directly; you get one fromjson.parse.- radlang structs and lists, which
json.parse<T>andjson.fromconvert to and from JSON automatically.
Serializing — json.from
json.from(value) : string Converts a radlang value to a JSON string. It handles primitives, null,
lists, structs (including nested structs and lists of structs), any, and
union types.
type Person {
string name,
int age,
bool is_student
}
fn main() {
sys.output(json.from(42)) // 42
sys.output(json.from("hi")) // "hi"
sys.output(json.from(true)) // true
sys.output(json.from([1, 2, 3])) // [1,2,3]
Person p = Person("Tim", 30, true)
sys.output(json.from(p)) // {"name":"Tim","age":30,"is_student":true}
} Typed parsing — json.parse<T>
json.parse(s: string) : JsonNode // untyped: returns a node to navigate
json.parse<T>(s: string) : T // typed: deserialize into T With a type argument, json.parse<T> deserializes directly into a primitive, a
struct, or a list — including nested structures. This is the easiest way to
consume JSON when you know its shape.
type Person {
string name,
int age,
bool is_student
}
type Group {
string group_name,
Person[] members
}
fn main() {
// Primitives
int n = json.parse<int>("42")
bool b = json.parse<bool>("true")
// Struct
Person p = json.parse<Person>("{\"name\":\"Ada\",\"age\":36,\"is_student\":false}")
sys.output(p.name) // Ada
// Nested struct with a list of structs
string data = json.from(Group("Devs", [Person("Tim", 30, true)]))
Group g = json.parse<Group>(data)
sys.output(g.members[0].name) // Tim
} Supported type arguments: int, uint, long, float, bool, string, char, byte, any struct type, and lists of those.
Note. Automatic deserialization into a union type is not supported — parse those manually with the navigation API below.
Manual navigation
When the shape is dynamic or unknown, parse to a JsonNode and walk it.
Traversing
json.parse(s) : JsonNode // root node
json.get(node, key) : JsonNode // object field by name
json.at(node, index) : JsonNode // array element by index
json.keys(node) : string[] // an object's keys
json.length(node) : int // array length / object field count
json.isNull(node) : bool // is this JSON null Extracting scalars
json.string(node) : string
json.int(node) : int
json.float(node) : float
json.bool(node) : bool fn main() {
JsonNode root = json.parse("{\"name\":\"Alice\",\"age\":28,\"tags\":[\"a\",\"b\"]}")
string name = json.string(json.get(root, "name")) // Alice
int age = json.int(json.get(root, "age")) // 28
JsonNode tags = json.get(root, "tags")
sys.output(json.length(tags).toString()) // 2
sys.output(json.string(json.at(tags, 0))) // a
string[] keys = json.keys(root)
for (string key in keys) {
sys.output(key) // name, age, tags
}
} Convenience getters
To fetch and extract a scalar in one call, use the combined getters. They are
shorthand for json.<scalar>(json.get(...)) / json.<scalar>(json.at(...)).
json.getString(node, key) : string // = json.string(json.get(node, key))
json.getInt(node, key) : int
json.getFloat(node, key) : float
json.getBool(node, key) : bool
json.atString(node, index) : string // = json.string(json.at(node, index))
json.atInt(node, index) : int
json.atFloat(node, index) : float
json.atBool(node, index) : bool fn main() {
JsonNode root = json.parse("{\"name\":\"Alice\",\"age\":28}")
sys.output(json.getString(root, "name")) // Alice
sys.output(json.getInt(root, "age")) // 28
} Quick reference
| Call | Returns | Purpose |
|---|---|---|
json.from(value) | string | Serialize any value to JSON |
json.parse(s) | JsonNode | Parse to a navigable node |
json.parse<T>(s) | T | Deserialize into a primitive/struct/list |
json.get(node, key) | JsonNode | Object field by name |
json.at(node, i) | JsonNode | Array element by index |
json.keys(node) | string[] | Object keys |
json.length(node) | int | Array length / field count |
json.isNull(node) | bool | Is JSON null |
json.string/int/float/bool(node) | scalar | Extract a scalar from a node |
json.getString/getInt/getFloat/getBool(node, key) | scalar | Get + extract by key |
json.atString/atInt/atFloat/atBool(node, i) | scalar | Get + extract by index |
Picking the right tool
- Producing JSON from your data →
json.from. - Consuming JSON of a known shape →
json.parse<T>into a struct. - Consuming dynamic/unknown JSON →
json.parsethenget/at/keysand the scalar extractors. - Parsing a union → navigate manually; typed parse doesn’t cover unions.