Skip to content

std/json

Import with import std/json.

enum Json {
    String String
    Int Int
    Float Float
    Bool Bool
    Arr List<Json>
    Obj Map<String, Json>
    Null
}

Tagged enum representing a parsed JSON value.

Variant names align with the Nomi-side type names rather than the JSON spec’s vocabulary — String String, Int Int, Float Float, Bool Bool keep the payload type visible at the pattern site so case jv { Int(n) -> ... } reads as the obvious shape match.

Number representation note: JSON has a single number type. Json distinguishes Int(Int) from Float(Float) at parse time based on whether the source has a decimal point, exponent, or would overflow Int. This makes integer fields easy to pattern-match while still preserving fractional values. Text round-trips are not representation-preserving for this split: encode(Json.Float(36.0)) emits 36.

fn decode(source: String): Result<Json, Json.DecodeError>

type function on Json

Parse a JSON document into a Json tree. Returns Err with a structured Json.DecodeError on syntax errors, type errors, or trailing content past the first value.

Numbers are decoded as Int when they fit in int64 with no fractional part / exponent; otherwise as Float. See the Json doc-comment for the rationale.

Interactive Tests

assert Json.decode("{\"ok\":true,\"n\":42}") == Result.Ok(
  Json.Obj{"ok" => Json.Bool(True), "n" => Json.Int(42)}
)
assert Result.err?(Json.decode("{"))
fn encode(jv: Json): String

type function on Json

Serialize a Json tree as compact JSON text (no whitespace between tokens).

Number-rendering note: Float(36.0) renders as "36", not "36.0". The Int/Float distinction captured by decode therefore does not necessarily survive a text round-trip.

Interactive Tests

assert Json.encode(
  Json.Obj{"ok" => Json.Bool(True), "n" => Json.Int(42)}
) == "{\"ok\":true,\"n\":42}"
fn to_dynamic(jv: Json): Dynamic

type function on Json

Converts a Json tree to Dynamic, preserving the same shape: strings, numbers, booleans, arrays, objects, and null.

Interactive Tests

dyn = Json.to_dynamic(Json.String("hello"))
assert Dynamic.as_string(dyn) == Result.Ok("hello")
fn kind(jv: Json): String

type function on Json

Human-readable shape name used in Json.ShapeError messages.

fn to_string(jv: Json): String

impl Display.to_string

fn inspect(jv: Json): String

impl Debug.inspect

struct Json.DecodeError {
    message: String
    line: Int
    col: Int
    offset: Int
}

Structured decode failure from decode. line and col are 1-based source coordinates for human display; offset is the 0-based byte position of the same spot, for programmatic tooling, and it points AT the offending character rather than one past it.

message is Nomi’s own text, not the host JSON parser’s. That is a designed contract, not an accident: the messages decode can produce are exactly

  • unexpected character 'x' — a locatable byte that cannot appear there
  • unexpected byte 0xff — that byte begins no valid UTF-8 rune
  • unexpected end of input: incomplete JSON value — input stopped mid-value
  • unexpected end of input: expected a JSON value — input was empty or blank
  • trailing content after JSON value — a second value follows the first
  • number out of range: <lexeme> — well-formed number, unrepresentable
  • malformed JSON — a failure shape with no position to report

and they are stable across host-toolchain upgrades. Code may match on them; the host parser’s own wording never appears here. A hand-constructed Json.DecodeError may of course carry any message.

Named with the Json prefix so it is distinct from decode errors returned by other modules.

fn to_string(e: Json.DecodeError): String

impl Display.to_string

fn inspect(value: Json.DecodeError): String

impl Debug.inspect

enum Json.Case {
    Snake
    Camel
    Pascal
    Kebab
    ScreamingSnake
}

Field-name casing for derived JSON object keys.

fn inspect(value: Json.Case): String

impl Debug.inspect

fn to_string(value: Json.Case): String

impl Display.to_string

fn equal?(a: Json.Case, b: Json.Case): Bool

impl Equatable.equal?

struct ToJson.Options {
    rename_all: Json.Case
}

Compile-time options accepted by derive ToJson for T with ....

fn inspect(value: ToJson.Options): String

impl Debug.inspect

fn equal?(a: ToJson.Options, b: ToJson.Options): Bool

impl Equatable.equal?

struct FromJson.Options {
    rename_all: Json.Case
}

Compile-time options accepted by derive FromJson for T with ....

fn inspect(value: FromJson.Options): String

impl Debug.inspect

fn equal?(a: FromJson.Options, b: FromJson.Options): Bool

impl Equatable.equal?

struct Json.ShapeError {
    path: List<String>
    expected: String
    got: String
}

Shape mismatch while converting an already-parsed Json tree into a typed Nomi value. Parse errors stay Json.DecodeError; shape errors describe the path inside the JSON tree where the typed conversion failed.

fn to_string(e: Json.ShapeError): String

impl Display.to_string

fn inspect(value: Json.ShapeError): String

impl Debug.inspect

fn shape_error_root(expected: String, got: Json): Json.ShapeError
fn shape_error_prepend(e: Json.ShapeError, segment: String): Json.ShapeError
interface ToJson {
    fn to_json(value: self): Json
}
fn to_json(value: self): Json

interface ToJson

interface FromJson {
    fn from_json(json: Json): Result<self, Json.ShapeError>
}
fn from_json(json: Json): Result<self, Json.ShapeError>

interface FromJson