Independent implementation of RFC 8259. Covers all JSON value types including full escape-sequence handling (surrogate pairs, \uXXXX). Does not support comments, trailing commas, or single-quoted strings by design. Round-trip property: Json.parse(v.into()) == v for any valid JsonValue.
let v = Json.parse("{\"name\": \"alice\", \"age\": 30}")
match v.as_object() {
Some(m) => {
assert(m.get("name") == Some(Str("alice")))
assert(m.get("age") == Some(Num(30.0)))
}
None => assert(false)
}
Types
JsonValue
stable since 0.1export type JsonValue
| Null
| Bool(bool)
| Num(f64)
| Str(str)
| Array([]JsonValue)
| Object(HashMap[str, JsonValue])
Recursive sum-type for arbitrary JSON documents. Structural equality is supported — v1 == v2 works for deep comparison including nested arrays and objects.
ParseJsonError
stable since 0.1export type ParseJsonError
| UnexpectedChar { line int; col int; found char }
| UnexpectedEof { line int; col int }
| InvalidEscape { line int; col int; seq str }
| InvalidUnicode { line int; col int; hex str }
| InvalidNumber { line int; col int; text str }
| DuplicateKey { line int; col int; key str }
| TrailingContent { line int; col int }
All variants carry 1-based line and col for diagnostic messages.
Parsing
Json.parse
stable since 0.1fn Json.parse(s str) Fail[ParseJsonError] -> JsonValue
Parses a JSON string. Throws Fail[ParseJsonError] on any lexical or syntactic error. Use a with Fail[ParseJsonError] block to handle errors.
Examples
let arr = Json.parse("[1, 2, 3]")
assert(arr.is_array())
let obj = Json.parse("{\"x\": true}")
match obj.as_object() {
Some(m) => assert(m.get("x") == Some(Bool(true)))
None => assert(false)
}
Constructors
Convenience static constructors for building JsonValue programmatically:
fn JsonValue.null() -> Self // Null
fn JsonValue.bool(b bool) -> Self // Bool(b)
fn JsonValue.num(n f64) -> Self // Num(n)
fn JsonValue.str(s str) -> Self // Str(s)
fn JsonValue.array(items []JsonValue) -> Self // Array(items)
fn JsonValue.object(fields HashMap[str, JsonValue]) -> Self // Object(fields)
Accessors
fn JsonValue @as_bool() -> Option[bool]
fn JsonValue @as_num() -> Option[f64]
fn JsonValue @as_str() -> Option[str]
fn JsonValue @as_array() -> Option[[]JsonValue]
fn JsonValue @as_object() -> Option[HashMap[str, JsonValue]]
Each returns None if the variant doesn't match. Combine with match or if let Some:
let v = Json.parse("42")
assert(v.as_num() == Some(42.0))
assert(v.as_str() == None)
Type guards
fn JsonValue @is_null() -> bool
fn JsonValue @is_bool() -> bool
fn JsonValue @is_num() -> bool
fn JsonValue @is_str() -> bool
fn JsonValue @is_array() -> bool
fn JsonValue @is_object() -> bool
Serialization
into — compact
stable since 0.1fn JsonValue @into() -> str
Serializes to compact JSON (single line, no extra whitespace). This is the D73 Into[str] implementation, so str.from(v) works too. Round-trip: Json.parse(v.into()) == v.
Examples
let v = Array([Num(1.0), Str("a")])
let s str = v.into()
assert(s == "[1,\"a\"]")
pretty
stable since 0.1fn pretty(v JsonValue) -> str
Pretty-prints with 2-space indentation. Not part of Into[str] — call explicitly when human-readable output is needed (logs, debug output). Use into() for compact canonical output.
Examples
let mut m = HashMap[str, JsonValue].new()
m.insert("name", Str("alice"))
let s = pretty(Object(m))
assert(s.contains("\n")) // multi-line