jmespath-d 1.0.0
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
Basic search
// 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:
| Function | Description |
|---|---|
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
- Edit source in
source/jmespath/ - Build:
dub build - Unit test:
dub test— runs inlineunittestblocks across all modules - Custom function test:
dub test :custom-func-test— runs custom function integration tests - Compliance:
dub run :compliance— runs all 968 compliance tests - Filter:
dub run :compliance -- tests/compliance <name>— run a single test file - Count:
dub run :count-tests— print per-file test counts - 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 File | Tests | Status |
|---|---|---|
| basic | 18 | ✅ PASS |
| benchmarks | 16 | ✅ PASS |
| boolean | 100 | ✅ PASS |
| current | 3 | ✅ PASS |
| escape | 8 | ✅ PASS |
| filters | 88 | ✅ PASS |
| functions | 182 | ✅ PASS |
| identifiers | 125 | ✅ PASS |
| indices | 59 | ✅ PASS |
| literal | 41 | ✅ PASS |
| multiselect | 53 | ✅ PASS |
| ormatch | 12 | ✅ PASS |
| pipe | 17 | ✅ PASS |
| slice | 41 | ✅ PASS |
| syntax | 136 | ✅ PASS |
| unicode | 4 | ✅ PASS |
| wildcard | 65 | ✅ PASS |
| Total | 968 |
Resources
- JMESPath Tutorial
- JMESPath Specification
- JMESPath Examples
- JMESPath Libraries — implementations in other languages
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:
| Repository | License | Notable contribution |
|---|---|---|
| jmespath/go-jmespath | Apache 2.0 | ormatch.json and core test suite |
| jmespath/jmespath.py | MIT | benchmarks.json and extended core tests |
| jmespath/jmespath.rs | MIT | Additional boolean.json, filters.json, and syntax.json cases |
- 1.0.0 released 2 months ago
- lempiji/jmespath-d
- MIT
- Authors:
- Sub packages:
- jmespath-d:count-tests, jmespath-d:compliance, jmespath-d:custom-func-test
- Dependencies:
- none
- Versions:
-
Show all 2 versions1.0.0 2026-Mar-16 ~main 2026-Mar-16 - 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