lens

    Typed JSON lenses and aggregate validation for MoonBit

    json
    lens
    validation
    moonbit
    Download zip
    Author
    Version
    0.2.0
    License
    MIT
    Last updated
    3 months ago
    Downloads
    1K

    #lens

    Reusable typed access and aggregate validation for MoonBit JSON values.

    The package keeps static types on Lens[T], while ObjectLens reads selected objects as Map[String, Json]. The returned map is copied so top-level mutations do not change the source document; nested Json values retain their normal sharing semantics. Validation only reports whether every requested read succeeds; it does not infer or construct application types from runtime definitions. After successful validation, read values through the original lenses.

    #Typed access

    test {
    let document = @json.parse(
    "{\"user\":{\"name\":\"Ada\",\"age\":37,\"active\":true}}",
    )
    let user = object("user")
    let name_lens = user.string("name")
    let age_lens = user.int("age")
    let active_lens = user.bool("active")

    inspect(name_lens.get(document), content="Ada")
    inspect(age_lens.get(document), content="37")
    inspect(active_lens.get(document), content="true")
    }

    Lens::get raises LensError(Issue) when traversal or decoding fails. Each Issue contains an RFC 6901 pointer, a structured IssueCode, and optional diagnostic context.

    test {
    let document = @json.parse("{\"user\":{}}")
    try {
    object("user").string("name").get(document) |> ignore
    fail("expected LensError")
    } catch {
    LensError(issue) =>
    inspect(issue.pointer.to_string(), content="/user/name")
    _ => fail("unexpected error")
    }
    }

    #Arrays and presence

    Compose an item lens with array to decode every array element. Item failures include their zero-based index in the reported JSON Pointer.

    test {
    let document = @json.parse("{\"names\":[\"Ada\",\"Grace\"]}")
    debug_inspect(
    root().string("names").array().get(document),
    content="[\"Ada\", \"Grace\"]",
    )
    }

    nullable, optional, and nullish keep missing properties distinct from explicit JSON null:

    Input stateRequired stringnullableoptionalnullish
    MissingerrorerrorNoneNone
    nulltype errorNonetype errorNone
    StringvalueSome(T)Some(T)Some(T)

    Here, missing means that the selected leaf property is absent. A missing intermediate object remains an error so optional values cannot hide an invalid surrounding structure.

    #Validation

    LensTrait exposes only the type-erased check operation required by aggregate validation. Lens[T] and ObjectLens implement it, so heterogeneous lenses can be passed directly to validate. Every check runs, and failures are returned in input order.

    test {
    let document = @json.parse(
    "{\"user\":{\"name\":\"Ada\",\"age\":37}}",
    )
    let user = object("user")
    let name_lens = user.string("name")
    let age_lens = user.int("age")

    match validate(document, [user, name_lens, age_lens]) {
    Valid => {
    let name : String = name_lens.get(document)
    let age : Int = age_lens.get(document)
    inspect((name, age), content="(Ada, 37)")
    }
    Invalid(issues) => fail("unexpected issues: \{issues}")
    }
    }

    Validation::Valid carries no decoded value. Validation::Invalid carries only Array[Issue].

    #Numeric behavior

    number returns the existing Double stored in Json::Number without further validation, including non-finite values. int delegates directly to MoonBit's standard Double::to_int conversion without package-level validation, inheriting its truncation, saturation, and special-value behavior. The package never reparses the retained JSON number text.

    #Current scope

    The API supports object-property traversal; String, Bool, Double, standard-converted Int, and raw Json values; typed arrays; and nullable, optional, and nullish values. Refinements, alternatives, and mutation such as set are reserved for later phases.

    See the design document for the detailed contract and roadmap. A Japanese translation is available at docs/design.ja.md.

    LensTrait

    pub trait LensTrait {
    fn check(Self, Json) -> Unit raise LensError
    }

    A type-erased lens read used only for aggregate validation.

    LensError

    pub(all) suberror LensError {
    LensError(Issue)
    } derive(Eq,
    Debug
    )

    The typed error raised when a lens cannot read its selected value.

    Decoder

    type Decoder[T]

    Issue

    pub(all) struct Issue {
    pointer : Pointer
    code : IssueCode
    message : String?
    } derive(Eq,
    Debug
    )

    A structured diagnostic containing the exact pointer and failure reason.

    IssueCode

    pub(all) enum IssueCode {
    MissingProperty
    TypeMismatch(expected~ : JsonKind, actual~ : JsonKind)
    IndexOutOfBounds(index~ : Int, length~ : Int)
    ConstraintViolation(code~ : String)
    ExternalDecode
    } derive(Eq,
    Debug
    )

    A machine-readable reason why a lens or validation check failed.

    JsonKind

    pub(all) enum JsonKind {
    Null
    Boolean
    Number
    String
    Array
    Object
    } derive(Eq,
    Debug
    )

    The JSON value category used by structured lens diagnostics.

    Lens

    pub struct Lens[T] {
    // private fields
    }

    A reusable typed accessor for one location in a JSON document.
    impl LensTrait for Lens[T]

    Lens::array

    fn[T] Lens::array(self : Lens[T]) -> Lens[Array[T]]

    Returns a lens that decodes every array item with this lens's decoder.

    Lens::get

    fn[T] Lens::get(self : Lens[T], document : Json) -> T raise LensError

    Reads and decodes this lens from a JSON document.

    Lens::nullable

    fn[T] Lens::nullable(self : Lens[T]) -> Lens[T?]

    Returns None for JSON null while preserving missing-property errors.

    Lens::nullish

    fn[T] Lens::nullish(self : Lens[T]) -> Lens[T?]

    Returns None for either a missing selected path or JSON null.

    Lens::optional

    fn[T] Lens::optional(self : Lens[T]) -> Lens[T?]

    Returns None for a missing selected path while rejecting JSON null.

    ObjectLens

    pub struct ObjectLens {
    // private fields
    }

    A location in a JSON document from which typed child lenses can be created.

    ObjectLens::bool

    fn ObjectLens::bool(self : ObjectLens, key : String) -> Lens[Bool]

    Returns a boolean lens positioned at a child property.

    ObjectLens::get

    fn ObjectLens::get(self : ObjectLens, document : Json) -> Map[String, Json] raise LensError

    Reads this object lens from a JSON document.

    ObjectLens::int

    fn ObjectLens::int(self : ObjectLens, key : String) -> Lens[Int]

    Returns an integer lens that delegates conversion to Double::to_int.

    ObjectLens::json

    fn ObjectLens::json(self : ObjectLens, key : String) -> Lens[Json]

    Returns a raw JSON lens positioned at a child property.

    ObjectLens::number

    fn ObjectLens::number(self : ObjectLens, key : String) -> Lens[Double]

    Returns a finite-number lens positioned at a child property.

    ObjectLens::object

    fn ObjectLens::object(self : ObjectLens, key : String) -> ObjectLens

    Returns an object lens positioned at a child property.

    ObjectLens::string

    fn ObjectLens::string(self : ObjectLens, key : String) -> Lens[String]

    Returns a string lens positioned at a child property.

    Pointer

    pub struct Pointer {
    // private fields
    } derive(Eq,
    Debug
    )

    An immutable JSON Pointer identifying a location in a JSON document.

    Pointer::to_string

    fn Pointer::to_string(self : Pointer) -> String

    Returns the RFC 6901 string representation of this pointer.

    Validation

    pub enum Validation {
    Valid
    Invalid(ReadOnlyArray[Issue])
    } derive(Eq,
    Debug
    )

    The outcome of validating a JSON document with type-erased checks.

    Valid carries no decoded value. Use the original typed lenses to read the document after validation succeeds.

    object

    fn object(key : String) -> ObjectLens

    Returns an object lens positioned at a root property.

    root

    fn root() -> ObjectLens

    Returns an object lens positioned at the document root.

    validate

    fn validate(document : Json, lenses : Array[&LensTrait]) -> Validation

    Runs every lens check and returns all issues in input order.

    This function never constructs or returns application values.

    Powered by MoonBit

    Site sourceReport issueModulesSkillsBuild queueStatistics

    © 2026 mooncakes.io