
## Namespace rule

`import json` gates access to the `Json`, `JsonValue`, `JsonError`, `JsonReader`,
and `JsonStreamWriter` types. Use type-static methods and instance methods:

```vary
import json

let j = Json.parse("{\"name\": \"Alice\"}").unwrap()
let text = j.stringify()
```

The compiler enforces the import: using `Json.parse()` without `import json`
is a compile error.

## Tree API

`Json.parse()` returns a typed `Result[Json, JsonError]`:

```vary
let result = Json.parse("{\"name\": \"Alice\", \"age\": 30}")
if result.is_ok() {
    let json = result.unwrap()
    let name = json.get_str("name")     # Str? "Alice"
    let age = json.get_int("age")       # Int? 30
}
```

Convenience accessors: `get_str`, `get_int`, `get_float`, `get_bool`, `get_list`.

### Static methods

| **Method** | **Returns** | **Description** |
|--------|---------|-------------|
| `Json.parse(text)` | `Result[Json, JsonError]` | Parse a JSON string |
| `Json.parse_bytes(b)` | `Result[Json, JsonError]` | Parse UTF-8 bytes |
| `Json.from_str(s)` | `Json` | Create a string value |
| `Json.from_int(i)` | `Json` | Create an integer value |
| `Json.from_float(d)` | `Json` | Create a float value |
| `Json.from_bool(b)` | `Json` | Create a boolean value |
| `Json.null_value()` | `Json` | Create a null value |
| `Json.empty_object()` | `Json` | Create an empty mutable object |

### Instance methods

| **Method** | **Returns** | **Description** |
|--------|---------|-------------|
| `.stringify()` | `Str` | Compact JSON string |
| `.stringify_pretty()` | `Str` | Indented JSON string |
| `.stringify_to_bytes()` | `Bytes` | Compact JSON as UTF-8 bytes |
| `.get_path(path)` | `Json?` | Dot-notation path access (e.g., `"items.0.name"`) |
| `.require_path(path)` | `Result[Json, JsonError]` | Path access with error context |

JSON Pointer (RFC 6901) navigates nested structures:

```vary
let json = Json.parse("{\"a\":{\"b\":[1,2,3]}}").unwrap()
let val = json.pointer_get("/a/b/1")
```

Require methods return `Result[T, JsonError]`:

```vary-snippet
let name = json.require_str("/user/name")
```

## Streaming API

For large documents, use `JsonReader` for pull-based parsing and `JsonStreamWriter` for incremental building:

```vary
let reader = JsonReader.from_str('{"name":"Alice"}').unwrap()
let token = reader.next().unwrap()
reader.close()
```

```vary
let w = JsonStreamWriter.create()
w.begin_object()
w.key("name")
w.str_value("Alice")
w.end_object()
let json = w.finish()
```

## Mutable building

Build JSON objects programmatically:

```vary
let obj = Json.empty_object()
obj.set_str("name", "Alice")
obj.set_int("age", 30)
obj.set_bool("active", True)
```

| **Method** | **Returns** | **Description** |
|--------|---------|-------------|
| `Json.empty_object()` | `Json` | Create an empty mutable object |
| `.set_str(key, value)` | `Json` | Set a string field |
| `.set_int(key, value)` | `Json` | Set an integer field |
| `.set_bool(key, value)` | `Json` | Set a boolean field |
| `.set_list(key, value)` | `Json` | Set a list field |
| `.set(key, json)` | `Json` | Set a nested Json value |

All setters return `self` for method chaining.

## Typed decode DSL

For structured, validated JSON extraction, use `Decoder` (gated by `import json`). See [JSON decode](/docs/json-decode/) for the full reference.

```vary-snippet
import json

let node = json.decode("{\"name\": \"Alice\", \"age\": 30}")?
let name = node.field("name").str()?        # Result[Str, DecodeError]
let age = node.field("age").int()?          # Result[Int, DecodeError]
let role = node.field("role").str_or("guest")  # Str (default if missing)
```

Navigate nested structures with chained `.field()` calls. Errors include the full
path (e.g., `"config.db.port"`) and expected/actual types.

| **Method** | **Description** | **Returns** |
|------------|-----------------|-------------|
| `str()` | Decode as string | `Result[Str, DecodeError]` |
| `int()` | Decode as integer | `Result[Int, DecodeError]` |
| `float()` | Decode as float | `Result[Float, DecodeError]` |
| `bool()` | Decode as boolean | `Result[Bool, DecodeError]` |
| `object()` | Decode as object | `Result[Decoder, DecodeError]` |
| `list()` | Decode as list of nodes | `Result[List[Decoder], DecodeError]` |
| `enum(variants)` | Decode as one of allowed strings | `Result[Str, DecodeError]` |
| `str_or_none()` | String if present | `Str?` |
| `int_or_none()` | Integer if present | `Int?` |
| `float_or_none()` | Float if present | `Float?` |
| `bool_or_none()` | Boolean if present | `Bool?` |
| `object_or_none()` | Object if present | `Decoder?` |
| `list_or_none()` | List if present | `List[Decoder]?` |
| `str_or(default)` | String with fallback | `Str` |
| `int_or(default)` | Integer with fallback | `Int` |
| `float_or(default)` | Float with fallback | `Float` |
| `bool_or(default)` | Boolean with fallback | `Bool` |
| `field(name)` | Navigate to child field | `Decoder` |
| `at(index)` | Navigate to list element | `Result[Decoder, DecodeError]` |
| `keys()` | List object keys | `List[Str]` |
| `size()` | Number of elements | `Int` |
| `path()` | Current node path | `Str` |
| `validate_int(msg, check)` | Validate integer with predicate | `Result[Int, DecodeError]` |
| `validate_str(msg, check)` | Validate string with predicate | `Result[Str, DecodeError]` |
| `transform_str(fn)` | Transform string value | `Result[Str, DecodeError]` |

`DecodeError` has `.path()`, `.expected()`, `.actual()`, and `.message()` methods.
