jmespath-d ~main

JMESPath implementation in D


To use this package, run the following command in your project's root directory:

Manual usage
Put the following dependency into your project's dependences section:


This package provides sub packages which can be used individually:

jmespath-d:count-tests - Count tests in tests/compliance

jmespath-d:compliance - Run JMESPath compliance tests

jmespath-d:custom-func-test - Custom function integration tests

jmespath-d

A JMESPath implementation in the D Programming Language.

JMESPath (pronounced "james path") is a query language for JSON that lets you declaratively extract and transform elements from a JSON document.

What is JMESPath?

Given a document:

{"foo": {"bar": "baz"}}

The expression foo.bar returns "baz".

JMESPath supports list indexing, wildcards, projections, filters, pipes, and more:

foo.bar[0]          → first element of an array
foo.bar[*].name     → projection: collect 'name' from every element
foo[?age > `30`]    → filter: keep only elements where age > 30
foo | length(@)     → pipe the result of 'foo' into length()

See the JMESPath tutorial and specification for the full language reference.

Installation

Add jmespath-d to your project's dub.json:

{
    "dependencies": {
        "jmespath-d": "~>1.0.0"
    }
}

Or with dub.sdl:

dependency "jmespath-d" version="~>1.0.0"

Usage

Common imports

import jmespath.api;   // search(), compile(), JMESPathOptions
import jmespath.value; // JMESPathValue wrapper around std.json.JSONValue
// One-shot search against a JSON string
JMESPathValue result = search("foo.bar", `{"foo": {"bar": "baz"}}`);
assert(result == jmesValue("baz"));

// Compatible with std.json.JSONValue
import std.json : JSONValue;

JSONValue json = result.data;
string baz = result.str;
assert(baz == "baz");

Compile and reuse

Compiling an expression up-front avoids reparsing on every call:

JMESPath expr = compile("foo.bar[0]");
assert(expr.search(`{"foo": {"bar": [1, 2, 3]}}`) == jmesValue(1L));
assert(expr.search(`{"foo": {"bar": [9, 8, 7]}}`) == jmesValue(9L));

Wildcards and projections

// Collect a field from every object in an array
JMESPathValue r = search("people[*].name",
    `{"people": [{"name": "Alice"}, {"name": "Bob"}]}`);
// r is ["Alice", "Bob"]

Filter expressions

// Keep items matching a condition
JMESPathValue r = search("orders[?price > `100`]",
    `{"orders": [{"price": 50}, {"price": 150}, {"price": 200}]}`);
// r is [{"price": 150}, {"price": 200}]

Error handling

All errors are subclasses of JMESPathException:

import jmespath.error; // ParseException, LexerException, EvalException, JMESPathException

try
{
    JMESPathValue r = search("foo[?bad expression", `{}`);
}
catch (ParseException e)  { /* malformed expression */ }
catch (LexerException e)  { /* unrecognised token */ }
catch (EvalException e)   { /* runtime type error */ }
catch (JMESPathException e) { /* base class */ }

Compile-Time Evaluation (CTFE)

All core functions are pure, making them eligible for D's compile-time function evaluation. Constant expressions are fully evaluated at compile time with zero runtime cost:

// Expression compiled at compile time — ASTNode is a compile-time constant
enum expr = compile("foo[*].bar");

// Full search evaluated at compile time
enum result = search("length(@)", `[1, 2, 3]`);
static assert(result == jmesValue(3L));

// Mix: compile-time compilation, runtime evaluation
void processData(string json)
{
    enum expr2 = compile("a.b.c");
    auto r = expr2.search(json);   // parse + eval at runtime, no recompile
}

Custom Functions

jmespath-d lets you register your own functions and use them alongside the built-in ones. Custom functions work at both runtime and compile time (CTFE).

import jmespath.functions; // JMESPathOptions, FunctionRegistry, JMESPathFunction
import jmespath.ast : ASTNode;
import jmespath.error : EvalException;

// 1. Define a custom function
static JMESPathValue myDouble(
    JMESPathValue[] args,
    JMESPathValue delegate(scope ref const ASTNode, JMESPathValue) _,
)
{
    if (args.length != 1 || !args[0].isNumber())
        throw new EvalException("double() requires 1 number argument");
    return jmesValue(args[0].toNumber() * 2.0);
}

// 2. Register it in a FunctionRegistry via JMESPathOptions
JMESPathOptions opts;
opts.customFunctions.register("double", &myDouble);

// 3. Use it in a JMESPath expression
auto result = search("double(foo)", `{"foo": 5}`, opts);
assert(result == jmesValue(10.0));

// Compiled expressions also accept JMESPathOptions
auto jp = compile("double(val)");
assert(jp.search(`{"val": 7}`, opts) == jmesValue(14.0));

CTFE-compatible custom functions

Because JMESPathFunction is a plain function pointer (not a delegate), custom functions work at compile time:

enum ctfeOpts = jmesPathOptions(["double": &myDouble]);

enum ctfeResult = search("double(`3`)", `{}`, ctfeOpts);
static assert(ctfeResult == jmesValue(6.0));

Built-in Functions

All functions defined in the JMESPath specification are implemented:

FunctionDescription
abs(number)Absolute value
avg(array)Average of a number array
ceil(number)Round up to nearest integer
contains(subject, search)True if array or string contains the value
ends_with(string, suffix)True if string ends with suffix
floor(number)Round down to nearest integer
join(glue, strarray)Join an array of strings with a separator
keys(object)Sorted array of object keys
length(subject)Length of string, array, or object
map(expr, array)Apply an expression reference to each element
max(array)Maximum value (numbers or strings)
max_by(array, expr)Element with the maximum key per expression
merge(object...)Merge objects left-to-right
min(array)Minimum value (numbers or strings)
min_by(array, expr)Element with the minimum key per expression
not_null(value...)First non-null argument
reverse(subject)Reverse a string or array
sort(array)Sort a homogeneous array
sort_by(array, expr)Sort by a key expression
starts_with(string, prefix)True if string starts with prefix
sum(array)Sum of a number array
to_array(value)Wrap non-arrays in a one-element array
to_number(value)Parse a string to a number, pass numbers through
to_string(value)Stringify any value as JSON
type(value)Type name: "null", "boolean", "number", "string", "array", or "object"
values(object)Array of object values

Architecture

source/jmespath/
├── api.d            # Public API: search(), compile()
├── lexer.d          # Tokenizer: expression string → tokens
├── parser.d         # Pratt parser: tokens → AST
├── ast.d            # AST node definitions
├── interpreter.d    # Tree-walking interpreter
├── functions.d      # Built-in functions, JMESPathOptions, FunctionRegistry
├── error.d          # Exception hierarchy
└── value.d          # JMESPathValue wrapper around std.json.JSONValue

Processing Pipeline

Expression string → Lexer → Tokens → Parser → AST → Interpreter → Result

Building and Testing

# Build the library
dub build

# Run inline unittest blocks (all library modules)
dub test

# Run custom function integration tests
dub test :custom-func-test

# Run all 968 compliance tests
dub run :compliance

# Run compliance tests for a single file
dub run :compliance -- tests/compliance basic

# Print per-file test counts
dub run :count-tests

# Run all checks at once (build + unittest + release build + compliance)
bash scripts/test.sh

Development Flow

  1. Edit source in source/jmespath/
  2. Build: dub build
  3. Unit test: dub test — runs inline unittest blocks across all modules
  4. Custom function test: dub test :custom-func-test — runs custom function integration tests
  5. Compliance: dub run :compliance — runs all 968 compliance tests
  6. Filter: dub run :compliance -- tests/compliance <name> — run a single test file
  7. Count: dub run :count-tests — print per-file test counts
  8. All at once: bash scripts/test.sh

Compliance Test Format

Tests are in tests/compliance/*.json. Each file contains an array of test suites:

[{
    "given": { "foo": "bar" },
    "cases": [
        {
            "expression": "foo",
            "result": "bar"
        },
        {
            "expression": "bad[",
            "error": "parse"
        }
    ]
}]

Compliance Status

968/968 tests passing (100%)

Test FileTestsStatus
basic18✅ PASS
benchmarks16✅ PASS
boolean100✅ PASS
current3✅ PASS
escape8✅ PASS
filters88✅ PASS
functions182✅ PASS
identifiers125✅ PASS
indices59✅ PASS
literal41✅ PASS
multiselect53✅ PASS
ormatch12✅ PASS
pipe17✅ PASS
slice41✅ PASS
syntax136✅ PASS
unicode4✅ PASS
wildcard65✅ PASS
Total968

Resources

License

MIT License

Acknowledgements

The compliance test suite in tests/compliance/ is collected and merged from the following open-source JMESPath implementations. Their test data is used here under their respective licenses:

RepositoryLicenseNotable contribution
jmespath/go-jmespathApache 2.0ormatch.json and core test suite
jmespath/jmespath.pyMITbenchmarks.json and extended core tests
jmespath/jmespath.rsMITAdditional boolean.json, filters.json, and syntax.json cases
Authors:
  • lempiji
Sub packages:
jmespath-d:count-tests, jmespath-d:compliance, jmespath-d:custom-func-test
Dependencies:
none
Versions:
1.0.0 2026-Mar-16
~main 2026-Mar-16
Show all 2 versions
Download Stats:
  • 0 downloads today

  • 0 downloads this week

  • 0 downloads this month

  • 2 downloads total

Score:
0.0
Short URL:
jmespath-d.dub.pm