# Decode JSON into a refined record

URL: /docs/cookbook/json_refined
Markdown: /docs/cookbook/json_refined.md

> Decode an order once and let its field refinements reject invalid external data.

Generated by `cargo run -p agent_docs` from `compiler/examples/`. Edit the example files, then regenerate.

Decode an order once and let its field refinements reject invalid external data.

`Json.decode` infers `Order` from the return annotation and checks both the record
shape and the predicates on `item` and `quantity`. Its `Result` keeps malformed
JSON, missing fields and invalid quantities recoverable; `main` handles both
branches and reports the decoding path. Use `from` only for trusted values where a
failed predicate should be a defect. No permissions are needed.

The harness runs `main` (expecting `2 x tea`) and both tests. The tests cover a valid
record, invalid JSON, an empty name, missing quantity and both out-of-range bounds.

[Source project](https://github.com/icantcodefyi/sakuralang/tree/master/compiler/examples/json_refined) · harness mode: `run + test`.

From the repository root, with this checkout's `sakura` on `PATH`:

```sh
cd compiler/examples/json_refined
sakura check --json
sakura run
sakura test --json
```

### Manifest and permissions [#manifest-and-permissions]

```toml
[package]
name = "json_refined"
version = "0.1.0"
edition = "2026"

[permissions]
```

### Source and tests [#source-and-tests]

`src/main.sakura` includes all required imports and tests.

```sakura test id=cookbook-json_refined project=json_refined
// milestone: M3
// mode: run
// spec: design §17.145

import std/json.{DecodeError}

type Quantity = Int where it >= 1 && it <= 100
type Order = { item: String where String.len(it) > 0, quantity: Quantity }

fn read_order(text: String) -> Result<Order, DecodeError> { Json.decode(text) }

pub fn main() -> Unit ! {Console} {
  match read_order("\{\"item\":\"tea\",\"quantity\":2\}") {
    Ok(order) => Console.print("{order.quantity} x {order.item}"),
    Err(err) => Console.print("invalid order at {err.path}: {err.message}"),
  }
}

test "JSON decoding validates the record fields" {
  expect_eq(
    read_order("\{\"item\":\"tea\",\"quantity\":2\}"),
    Ok(Order { item: "tea", quantity: 2 }),
  )
}

test "malformed JSON and invalid fields are recoverable" {
  expect(Result.is_err(read_order("not json")))
  expect(Result.is_err(read_order("\{\"item\":\"tea\",\"quantity\":0\}")))
  expect(Result.is_err(read_order("\{\"item\":\"tea\",\"quantity\":101\}")))
  expect(Result.is_err(read_order("\{\"item\":\"\",\"quantity\":1\}")))
  expect(Result.is_err(read_order("\{\"item\":\"tea\"\}")))
}
```

### Expected stdout [#expected-stdout]

```text
2 x tea
```

### Compiler provenance and regeneration [#compiler-provenance-and-regeneration]

These eight projects teach supported Sakura APIs on the JavaScript backend. Each
project is exercised offline by `cargo test -p sakura_cli --test examples`;
HTTP and LLM exchanges are synthetic committed cassettes, with no network or API keys.
Use Node 26 for execution, including its built-in SQLite implementation.

The initial API baseline is compiler revision
[5cae349516c21714d90d36e87f1cfd6debe980c6](https://github.com/icantcodefyi/sakuralang/commit/5cae349516c21714d90d36e87f1cfd6debe980c6).
CI reruns these projects against the compiler in the same checkout. Build that
checkout's CLI and run `sakura --version` and `sakura prompt` before adapting a recipe;
pre-alpha APIs can change. An installed skill's frontmatter records its compiler
version and build revision. These teaching examples are separate from the agent
evaluation tasks.

The source of each page is its example's `recipe.md`, `sakura.toml`,
`src/main.sakura` and fixture files. Run `cargo run -p agent_docs` from `compiler/`
to regenerate the cookbook and skill reference. Edit those sources, not the rendered pages.
