20 releases (6 breaking)
Uses new Rust 2024
| new 0.7.0 | Dec 18, 2025 |
|---|---|
| 0.6.3 | Dec 15, 2025 |
| 0.5.0 | Dec 12, 2025 |
| 0.4.1 | Dec 10, 2025 |
| 0.1.0 | Dec 6, 2025 |
#205 in Parser implementations
567 downloads per month
Used in 2 crates
1MB
20K
SLoC
jmespath_extensions
Extended functions for JMESPath queries in Rust. 320+ functions for strings, arrays, dates, hashing, encoding, and more.
Built on jmespath.rs
This crate extends the jmespath crate by @mtdowling, which provides the complete Rust implementation of the JMESPath specification. All spec-compliant parsing, evaluation, and the 26 built-in functions come from that foundational library—we simply add extra functions on top.
If you only need standard JMESPath functionality, use jmespath directly.
Want to try it out? Install the jpx CLI tool:
# Homebrew (macOS/Linux)
brew install joshrotenberg/brew/jpx
# Or cargo
cargo install jpx
# Then use it!
echo '{"name": "world"}' | jpx 'upper(name)'
# "WORLD"
Non-Standard Extensions - Not Portable
This crate provides custom extension functions that are NOT part of the JMESPath specification. Queries using these functions will NOT work in other JMESPath implementations (Python, JavaScript, Go, AWS CLI, Ansible, etc.).
JMESPath Spec vs This Library
| JMESPath Specification | jmespath_extensions | |
|---|---|---|
| Functions | 26 built-in functions | 320+ extension functions |
| Portability | Works everywhere (Python, JS, Go, AWS CLI, Ansible) | Rust only |
| Design | Minimal, query-focused | Transformation-heavy, practical |
| Governance | JEP process, multi-year consensus | Opinionated, can change |
| Philosophy | "Spec purity" | "Useful > Pure" |
What This Means
-
Not portable: Queries using
upper(),map_expr(),haversine(), etc. won't work in AWS CLI's--query, Ansible filters, or any other JMESPath implementation. -
No spec backing: Function names, signatures, and behaviors are our decisions. While we align with JEPs where possible (
items,find_first), many functions are novel. -
Expression functions are unique:
map_expr,filter_expr,sort_by_expretc. leverage Rust runtime access—these don't exist in any JMESPath spec or implementation.
Use Cases
This library is ideal for:
- Backend data transformation: Reshape API responses, filter datasets, compute derived fields
- Configuration processing: Query and transform JSON/YAML configs with complex logic
- ETL pipelines: Extract, transform, and validate data with expressive queries
- Log/event processing: Filter and aggregate structured log data
- CLI tools: Build jq-like tools with domain-specific functions
- Embedded queries: Let users write safe, sandboxed data queries in your application
For Portable Queries
Use only the 26 standard JMESPath built-in functions:
abs, avg, ceil, contains, ends_with, floor, join, keys, length, map, max, max_by, merge, min, min_by, not_null, reverse, sort, sort_by, starts_with, sum, to_array, to_number, to_string, type, values
Overview
This crate provides 320+ additional functions beyond the standard JMESPath built-ins, organized into feature-gated categories.
Quick Start
use jmespath::Runtime;
use jmespath_extensions::register_all;
let mut runtime = Runtime::new();
runtime.register_builtin_functions();
register_all(&mut runtime);
// Now you can use extended functions in queries
let expr = runtime.compile("items[*].name | upper(@)").unwrap();
Runtime Function Registry
For applications that need runtime control over function availability (ACLs, config-based gating, introspection):
use jmespath::Runtime;
use jmespath_extensions::registry::{FunctionRegistry, Category};
let mut registry = FunctionRegistry::new();
// Register specific categories
registry.register_category(Category::String);
registry.register_category(Category::Math);
// Or register all available functions
// registry.register_all();
// Disable specific functions (e.g., for security policies)
registry.disable_function("md5");
registry.disable_function("sha256");
// Apply to runtime
let mut runtime = Runtime::new();
runtime.register_builtin_functions();
registry.apply(&mut runtime);
// Introspection - list available functions
for func in registry.functions() {
let type_label = if func.is_standard { "standard" } else { "extension" };
println!("[{}] {}: {}", type_label, func.name, func.description);
}
This enables:
- Runtime gating: Enable/disable functions via config instead of compile-time features
- ACL support: Disable specific functions for security policies
- Introspection: Query available functions with signatures, descriptions, examples, and whether they are standard JMESPath or extensions
jpx CLI
See jpx/README.md for full CLI documentation, or use jpx --help.
# Expression functions (the novel stuff!)
echo '{"users": [{"name": "alice", "age": 30}, {"name": "bob", "age": 25}]}' \
| jpx 'filter_expr(users, &age > `26`) | [].name'
# ["alice"]
# Strict mode - only standard JMESPath functions
echo '[1, 2, 3]' | jpx --strict 'length(@)'
# 3
# Function discovery
jpx --list-functions # List all 320+ functions
jpx --list-category expression # List expression functions
jpx --describe map_expr # Detailed function info
Features
All features are opt-in. Use default-features = false to select only what you need.
| Feature | Description | Dependencies |
|---|---|---|
full (default) |
All functions | All below |
core |
Essential functions, no external deps | None |
| Core Modules | ||
string |
upper, lower, split, replace, camel_case, etc. |
None |
array |
first, last, unique, chunk, zip, range, etc. |
None |
object |
items, pick, omit, deep_merge, flatten_keys, etc. |
None |
math |
round, sqrt, median, stddev, sin, cos, etc. |
None |
type |
type_of, is_string, is_empty, to_number, etc. |
None |
utility |
default, if, coalesce, now, now_ms, etc. |
None |
validation |
is_email, is_url, is_uuid, is_ipv4, is_ipv6 |
None |
path |
path_basename, path_dirname, path_ext, path_join |
None |
expression |
map_expr, filter_expr, sort_by_expr, group_by_expr, etc. |
None |
text |
word_count, reading_time, word_frequencies, etc. |
None |
| External Deps | ||
hash |
md5, sha1, sha256, crc32 |
md-5, sha1, sha2, crc32fast |
encoding |
base64_encode, base64_decode, hex_encode, hex_decode |
base64, hex |
regex |
regex_match, regex_extract, regex_replace |
regex |
url |
url_encode, url_decode, url_parse |
url, urlencoding |
uuid |
uuid (v4 generation) |
uuid |
rand |
random, shuffle, sample |
rand |
datetime |
parse_date, format_date, date_add, date_diff |
chrono |
fuzzy |
levenshtein, jaro_winkler, sorensen_dice, etc. |
strsim |
phonetic |
soundex, metaphone, double_metaphone, nysiis, etc. |
rphonetic |
geo |
geo_distance, geo_distance_km, geo_distance_miles, geo_bearing |
geoutils |
semver |
semver_parse, semver_compare, semver_satisfies, etc. |
semver |
network |
ip_to_int, cidr_contains, cidr_network, is_private_ip |
ipnetwork |
ids |
nanoid, ulid, ulid_timestamp |
nanoid, ulid |
duration |
parse_duration, format_duration, etc. |
None |
color |
hex_to_rgb, rgb_to_hex, lighten, darken, etc. |
None |
computing |
parse_bytes, format_bytes, bit_and, bit_or, etc. |
None |
jsonpatch |
json_patch, json_merge_patch, json_diff (RFC 6902/7396) |
json-patch |
multi-match |
match_any, match_all, match_which, match_count, replace_many |
aho-corasick |
Minimal Dependencies
[dependencies]
jmespath_extensions = { version = "0.2", default-features = false, features = ["core"] }
Specific Features
[dependencies]
jmespath_extensions = { version = "0.2", default-features = false, features = ["string", "array", "datetime"] }
Examples
String Manipulation
upper('hello') → "HELLO"
split('a,b,c', ',') → ["a", "b", "c"]
camel_case('hello_world') → "helloWorld"
Array Operations
first([1, 2, 3]) → 1
unique([1, 2, 1, 3]) → [1, 2, 3]
chunk([1, 2, 3, 4], `2`) → [[1, 2], [3, 4]]
Expression Functions
map_expr('name', users) → ["alice", "bob"]
filter_expr('age >= `18`', users) → [{...}, {...}]
sort_by_expr('score', items) → [{score: 1}, {score: 2}, ...]
Date/Time
now() → 1699900000
format_date(`0`, '%Y-%m-%d') → "1970-01-01"
date_add(`0`, `1`, 'days') → 86400
Fuzzy Matching
levenshtein('kitten', 'sitting') → 3
jaro_winkler('hello', 'hallo') → 0.88
sounds_like('Robert', 'Rupert') → true
Geospatial
geo_distance_km(`40.7128`, `-74.0060`, `51.5074`, `-0.1278`) → 5570.2
geo_bearing(`40.7128`, `-74.0060`, `51.5074`, `-0.1278`) → 51.2
Network
cidr_contains('192.168.0.0/16', '192.168.1.1') → true
is_private_ip('10.0.0.1') → true
JSON Patch (RFC 6902/7396)
json_patch({a: 1}, [{op: 'add', path: '/b', value: 2}]) → {a: 1, b: 2}
json_merge_patch({a: 1}, {b: 2}) → {a: 1, b: 2}
json_diff({a: 1}, {a: 2}) → [{op: 'replace', path: '/a', value: 2}]
Multi-Pattern Matching (Aho-Corasick)
match_any('hello world', ['world', 'foo']) → true
match_all('hello world', ['hello', 'world']) → true
match_which('hello world', ['hello', 'foo', 'world']) → ["hello", "world"]
match_count('abcabc', ['a', 'b']) → 4
replace_many('hello world', {hello: 'hi', world: 'earth'}) → "hi earth"
See the API documentation for complete function reference with examples.
JMESPath Community JEP Alignment
This crate aligns with several JMESPath Enhancement Proposals:
- JEP-014 (String Functions):
lower,upper,trim,trim_left,trim_right,pad_left,pad_right,replace,split,find_first,find_last - JEP-013 (Object Functions):
items,from_items,zip
Functions that align with JEPs have jep: Some("JEP-XXX") in their FunctionInfo metadata, accessible via the registry.
Additional functions extend well beyond these proposals. Some JEPs (like arithmetic operators) require parser changes and cannot be implemented as extension functions.
Development
Adding a New Function
-
Add metadata to
jmespath_extensions/functions.toml:[[functions]] name = "my_function" category = "string" # Must match an existing category description = "Brief description of what it does" signature = "string, number -> string" example = "my_function('hello', `3`) -> 'hellohellohello'" features = ["string"] # Feature flags that enable this function # Optional fields: # jep = "JEP-014" # If aligned with a JMESPath Enhancement Proposal # aliases = ["my_func"] # Alternative names -
Implement the function in the appropriate module (e.g.,
src/string.rs):define_function! { name = "my_function"; doc = "Brief description of what it does."; args = [value: Value, count: usize]; fn run(value, count) { // Implementation let s = value.as_string().ok_or_else(|| /* error */)?; Ok(Rc::new(Variable::String(s.repeat(count)))) } } -
Register the function in the module's
register_*function:pub fn register_string(runtime: &mut Runtime) { // ... existing registrations runtime.register_function(Box::new(MyFunction)); } -
Run the build to regenerate documentation:
cargo build # build.rs generates docs from functions.toml cargo test # Verify everything works cargo clippy --all-features
Adding a New Feature/Category
-
Add the feature to
Cargo.toml:[features] my_category = ["dep:some-crate"] # If it needs dependencies full = ["my_category", ...] # Add to full feature -
Create the module
src/my_category.rswith the standard structure -
Add to
src/lib.rs:#[cfg(feature = "my_category")] pub mod my_category; -
Add the category variant to
Categoryenum insrc/registry.rs -
Update
build.rsto handle the new category incategory_variant()
Contribution Guidelines
When proposing new functions, please ensure they meet these criteria:
-
Generally useful: Functions should solve common problems that many users encounter. Avoid highly specialized or niche use cases.
-
Minimal dependencies: Prefer zero dependencies. If a dependency is needed, limit to one well-maintained crate. Dependencies should be feature-gated.
-
Thoughtful naming: Function names should be:
- Clear and descriptive
- General enough to not imply overly specific behavior
- Consistent with existing naming conventions in the category
-
Fit existing categories: New functions should naturally belong to an existing feature/category. Proposing a new category requires strong justification and multiple related functions.
-
No duplicate functionality: Check that the function doesn't duplicate existing functionality or can be trivially composed from existing functions.
-
Include tests and examples: All new functions must include tests and a working example in
functions.toml.
Benchmarks
Run benchmarks with:
cargo bench --all-features
License
Licensed under either of:
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
Dependencies
~0.8–6MB
~97K SLoC