jmespath

    A standards-oriented JMESPath parser and JSON query engine in MoonBit.

    jmespath
    json
    query
    parser
    Download zip
    Version
    0.1.6
    License
    Apache-2.0
    Last updated
    13 days ago
    Downloads
    149

    No dependencies

    #MoonJMES

    简体中文 | English

    CI License

    MoonJMES is a native MoonBit implementation of JMESPath, the standard query language for JSON. It turns nested API responses, cloud inventories, audit records, and configuration files into smaller JSON results without embedding JavaScript or shipping data to a service.

    The engine passes 892/892 cases in the official JMESPath compliance suite at pinned revision 53abcc37901891cf4308fcd910eab287416c4609. The result is reproducible with node scripts/conformance.mjs; see docs/conformance.md.

    #Why this project

    MoonBit already has general JSON and jq-style tools, but the Mooncakes search at project start returned no JMESPath implementation. JMESPath is widely used in cloud and automation tooling, has a formal grammar, and has a language-neutral test suite. That gives this project a clear ecosystem boundary and an objective definition of compatibility.

    #Features

    • identifiers, quoted identifiers, current node, JSON and raw-string literals
    • object/list wildcards, flattening, projections, filters, indexes, and slices
    • pipes, comparisons, boolean expressions, multi-select lists and hashes
    • all standard scalar, collection, conversion, and expression-reference functions, including map, sort_by, min_by, and max_by
    • reusable compiled expressions and one-shot JSON/text APIs
    • batch execution with per-request structured errors
    • configurable expression, AST depth, evaluation-step, and result limits
    • a file/stdin CLI with deterministic JSON output
    • pure MoonBit engine; Node.js is used only by the CLI host adapter

    #Quick start

    Install the published library from Mooncakes:

    moon add JingLan0v0/jmespath

    The package documentation is available at mooncakes.io/docs/JingLan0v0/jmespath.

    moon add installs a library dependency, not a global CLI executable. This release supports the JS backend; CI pins MoonBit 0.10.14+7d59c7ec9 and Node.js 24.14.0 (see toolchain.json). To run the CLI, clone this repository and query the included file:

    git clone https://github.com/JingLan0v0/moonbit-jmespath.git cd moonbit-jmespath moon run cmd/main --target js -- "instances[?state == 'running'].{id: id, zone: zone}" examples/cloud-inventory.json

    Or pipe JSON through standard input:

    echo '{"items":[{"name":"A","price":9},{"name":"B","price":12}]}' \ | moon run cmd/main --target js -- 'items[?price >= `10`].name' -

    Result:

    [ "B" ]

    #Library API

    Import the package in moon.pkg:

    import {
    "JingLan0v0/jmespath" @jmespath,
    "moonbitlang/core/json",
    }

    let input = @json.parse("{\"people\":[{\"name\":\"Ada\",\"age\":36}]}")
    let query = @jmespath.compile("people[?age >= `18`].name")
    let result = query.search(input)

    Public entry points: compile, Compiled::search, search, search_json, and search_batch. Use stringify_bounded(result, pretty=true) to serialize library results with the same output safeguards as the CLI.

    The fragment above belongs inside a function that handles or propagates errors. For a complete executable with imports, custom limits, query reuse and error handling, run moon run examples/library --target js from this checkout. See the API and error guide.

    #Batch API and CLI

    Batch input is an array of independent requests:

    [ {"expression":"name","data":{"name":"Ada"}}, {"expression":"length()","data":null} ]

    moon run cmd/main --target js -- --batch examples/batch.json

    Each response has ok: true and result, or ok: false and a structured error. Invalid expressions and malformed request entries therefore do not discard other batch results. Invalid top-level JSON still exits with code 2. Batch mode exits 0 even when individual requests fail: inspect every ok field. The included file adds a third successful fallback query after the error.

    #Three runnable scenarios

    The examples directory covers cloud inventory selection, deployment-policy findings, and cost-report sorting. Run all three on Windows with ./examples/run.ps1; the script compares each result with committed JSON.

    #Resource limits and errors

    Defaults are 16,384 expression characters, AST depth 256, 1,000,000 evaluation steps, and 100,000 cumulatively materialized result elements. Custom limits must be valid, and AST depth cannot exceed 256. Batch input is capped at 10,000 requests; the CLI reads at most 16 MiB and decodes UTF-8 strictly, accepting an optional UTF-8 BOM.

    Version 0.1.3 also bounds active evaluation calls to 128, checks JSON input and output iteratively, and limits aggregate batch successes. The JSON guard limits depth to 256 (root at zero), expanded nodes to 1,000,000 and the conservative output estimate to 16 Mi UTF-16 units. See the exact resource contract.

    Errors use stable categories: syntax, invalid_json, invalid_type, invalid_arity, invalid_value, unknown_function, invalid_batch, and limit_exceeded. A library caller receives JmesPathError; the CLI writes a single diagnostic to stderr and exits with code 2. This describes single-query or whole-batch failures; per-item batch errors stay in the JSON response on stdout, as described above.

    #Verification

    moon fmt --check moon check --target js moon test --target js moon info --target js node scripts/conformance.mjs node scripts/conformance.mjs --release node scripts/verify.mjs

    The GitHub Actions matrix runs on Windows and Ubuntu with the pinned compiler. Architecture and trust boundaries are in docs/architecture.md.

    #September acceptance bug fixed in 0.1.2

    The rejected release could crash while parsing an expression made of 4,000 ! prefixes followed by @ (4,001 characters, below the default length limit). The parser checked AST depth only after recursion, so a three-request batch produced no output and exited with status 1 in both debug and release. The parser now enforces recursion depth while building the AST and checks the depth of each constructed node. The offending request returns a structured limit_exceeded error; the requests before and after it still return results. Single-query CLI mode exits with status 2 and a diagnostic instead of crashing. Regression tests cover the exact batch shape, single-query mode, nested grouping and the allowed depth boundary in both CLI builds. The original 0.1.1 release remains available for historical comparison.

    A Chinese project overview for contest information review is available in docs/project-introduction.zh-CN.md.

    #Project boundary

    #October hardening in 0.1.3

    The October audit reproduced a separate stack overflow in 0.1.2: 255 nested to_array(...) calls passed AST validation but crashed the debug evaluator. Execution now checks active call depth before descending. Short repeated [@,@] pipes could also expand exponentially during serialization while barely increasing the old result counter. An iterative output guard now counts each expanded occurrence; iterative JSON equality consumes the query work budget. join checks size before allocation, object producers charge keys before insertion, and batch successes share an output budget with room reserved for errors. Non-finite arithmetic returns invalid_value instead of invalid JSON.

    Verification covers 39 unit tests, three scenarios, and, in each CLI build mode, 22 adversarial checks plus 256 deterministic grammar mutations. Test subprocesses have a timeout and heap cap. CI enforces warning-free checks and exact official error categories in both modes, on Windows and Ubuntu, and saves downloadable JSON evidence. See acceptance evidence and competition status.

    MoonJMES implements the JMESPath specification. The 0.1.x line has no custom query extensions, keeping results portable across compliant JMESPath implementations. Version 0.1.5 is published on Mooncakes and verified from a fresh consumer in debug and release. Older versions remain available for historical comparison. See CHANGELOG.md.

    #Null semantics and usability fixes in 0.1.4

    An additional audit found behavior not covered by the pinned official vectors: multi-select lists/hashes must preserve a null input, while a subexpression must still evaluate its right-hand function on a null left result. For example, missing.not_null(@, 'fallback') now returns "fallback" for {}, and [foo] returns null for null input. Added regressions cover projections, map, error propagation, and non-null false-like values. Existing resource guards remain.

    Quick-start commands now use committed input files, a complete public API example runs in both modes, and the PowerShell scenario runner works outside the checkout. See CHANGELOG.md, usage and contributing. Release evidence is in acceptance.md.

    #Numeric conversion memory fix in 0.1.5

    to_number no longer parses arbitrary encoded arrays/objects merely to return null. An encoded 4.8 MB object array crashed 0.1.4 under a 192 MiB test heap in both modes; scalar prefiltering now preserves the batch's neighboring results. Numeric scanning also consumes the work budget. This is a conversion fix, not a universal memory guarantee: core transport decoding can still exhaust a constrained heap. See resource limits and the release evidence.

    #License

    Apache-2.0. See THIRD_PARTY_NOTICES.md for the verification-time relationship with the official test suite.

    JmesPathError

    pub(all) suberror JmesPathError {
    JmesPathError(Diagnostic)
    }

    CompareOp

    type CompareOp derive(Eq,
    Debug
    )

    CompareOp::equal

    fn CompareOp::equal(CompareOp, CompareOp) -> Bool

    CompareOp::not_equal

    fn CompareOp::not_equal(x : CompareOp, y : CompareOp) -> Bool

    Compiled

    pub struct Compiled {
    source : String
    expr : Expr
    limits : Limits
    }

    Compiled::search

    fn Compiled::search(self : Compiled, data : Json) -> Json raise JmesPathError

    Compiled::source

    fn Compiled::source(self : Compiled) -> String

    Diagnostic

    pub(all) struct Diagnostic {
    code : String
    offset : Int
    message : String
    } derive(Eq,
    Debug
    )

    Diagnostic::equal

    fn Diagnostic::equal(Diagnostic, Diagnostic) -> Bool

    Diagnostic::not_equal

    fn Diagnostic::not_equal(x : Diagnostic, y : Diagnostic) -> Bool

    Expr

    Expr::equal

    fn Expr::equal(Expr, Expr) -> Bool

    Expr::not_equal

    fn Expr::not_equal(x : Expr, y : Expr) -> Bool

    Expr::to_repr

    Limits

    pub(all) struct Limits {
    max_expression_chars : Int
    max_ast_depth : Int
    max_steps : Int
    max_results : Int
    } derive(Eq,
    Debug
    )

    Limits::equal

    fn Limits::equal(Limits, Limits) -> Bool

    Limits::not_equal

    fn Limits::not_equal(x : Limits, y : Limits) -> Bool

    Limits::to_repr

    Token

    Token::equal

    fn Token::equal(Token, Token) -> Bool

    Token::not_equal

    fn Token::not_equal(x : Token, y : Token) -> Bool

    Token::to_repr

    TokenKind

    type TokenKind derive(Eq,
    Debug
    )

    TokenKind::equal

    fn TokenKind::equal(TokenKind, TokenKind) -> Bool

    TokenKind::not_equal

    fn TokenKind::not_equal(x : TokenKind, y : TokenKind) -> Bool

    compile

    fn compile(source : String, limits? : Limits) -> Compiled raise JmesPathError

    default_limits

    fn default_limits() -> Limits

    parse_json_bounded

    fn parse_json_bounded(text : String) -> Json raise JmesPathError

    Preflight text allocation budgets, then parse JSON. This does not impose per-query JSON shape limits; batch callers retain independent item checks.
    fn search(expression : String, data : Json, limits? : Limits) -> Json raise JmesPathError

    search_batch

    fn search_batch(requests : Json, limits? : Limits) -> Json raise JmesPathError

    Runs independent queries from a JSON array of {expression, data} objects. Errors are returned per item, so one invalid query or malformed request does not abort the batch. The top-level input must still be an array.

    search_json

    fn search_json(expression : String, json_text : String, limits? : Limits) -> Json raise JmesPathError

    stringify_bounded

    fn stringify_bounded(value : Json, pretty? : Bool) -> String raise JmesPathError

    Serialize after checking nesting, expanded node count, and a conservative output-size estimate. Pretty output uses two spaces and is also bounded.

    version

    fn version() -> String