JSON Matching Expressions library that allows declarative extraction of elements from JSON documents using a query language
npx @tessl/cli install tessl/pypi-jmespath@1.0.0JMESPath (pronounced "james path") allows you to declaratively specify how to extract elements from a JSON document using a query language designed specifically for JSON. It provides a simple yet powerful syntax for navigating complex JSON structures, supporting operations like accessing nested objects, filtering arrays, projecting data from collections, and applying built-in functions for data transformation.
pip install jmespathimport jmespathFor accessing specific components:
from jmespath import search, compile, Options
from jmespath.exceptions import (
JMESPathError, ParseError, JMESPathTypeError,
IncompleteExpressionError, LexerError, ArityError,
VariadictArityError, EmptyExpressionError, UnknownFunctionError
)
from jmespath import functions # For custom functionsimport jmespath
# Simple path extraction
data = {'foo': {'bar': 'baz'}}
result = jmespath.search('foo.bar', data)
# Returns: 'baz'
# Array operations
data = {'users': [{'name': 'Alice', 'age': 30}, {'name': 'Bob', 'age': 25}]}
names = jmespath.search('users[*].name', data)
# Returns: ['Alice', 'Bob']
# Filtering with conditions
adults = jmespath.search('users[?age >= `30`]', data)
# Returns: [{'name': 'Alice', 'age': 30}]
# Compiled expressions for reuse
expression = jmespath.compile('users[*].name')
names1 = expression.search(data1)
names2 = expression.search(data2)JMESPath uses a multi-stage processing pipeline:
The architecture enables caching of parsed expressions for performance and provides comprehensive error reporting with precise location information.
Primary interface for searching JSON data with JMESPath expressions, supporting both one-time queries and compiled expressions for repeated use.
def search(expression, data, options=None):
"""
Search data using JMESPath expression.
Args:
expression (str): JMESPath expression string
data: Python data structure to search (dict, list, etc.)
options (Options, optional): Options for controlling evaluation
Returns:
any: Extracted data matching the expression
"""
def compile(expression):
"""
Compile JMESPath expression into reusable parsed expression.
Args:
expression (str): JMESPath expression string
Returns:
ParsedResult: Object with search(data, options=None) method
"""Control how JMESPath expressions are evaluated, including dictionary class selection and custom function registration.
class Options:
"""Options to control how a JMESPath expression is evaluated."""
def __init__(self, dict_cls=None, custom_functions=None):
"""
Initialize evaluation options.
Args:
dict_cls: Class to use when creating dictionaries (default: dict)
custom_functions: Custom function registry instance
"""Comprehensive error handling for different types of JMESPath failures, from syntax errors to type mismatches.
class JMESPathError(ValueError):
"""Base exception for all JMESPath errors."""
class ParseError(JMESPathError):
"""Invalid JMESPath expression parse error."""
def __init__(self, lex_position, token_value, token_type, msg="Invalid jmespath expression"):
"""
Args:
lex_position (int): Position in expression where error occurred
token_value (str): Token value that caused error
token_type (str): Type of token that caused error
msg (str): Error message
"""
class IncompleteExpressionError(ParseError):
"""Incomplete JMESPath expression error."""
class LexerError(ParseError):
"""Lexical analysis error."""
class EmptyExpressionError(JMESPathError):
"""Empty JMESPath expression error."""
class ArityError(ParseError):
"""Wrong number of arguments to function."""
def __init__(self, expected, actual, name):
"""
Args:
expected (int): Expected number of arguments
actual (int): Actual number of arguments provided
name (str): Function name
"""
class VariadictArityError(ArityError):
"""Too few arguments for variadic function."""
class JMESPathTypeError(JMESPathError):
"""Type error in function argument."""
def __init__(self, function_name, current_value, actual_type, expected_types):
"""
Args:
function_name (str): Name of function
current_value: The problematic value
actual_type (str): Actual type found
expected_types (list): Expected types
"""
class UnknownFunctionError(JMESPathError):
"""Unknown function name error."""JMESPath provides a comprehensive set of built-in functions organized by functionality. All functions are used within JMESPath expressions, not called directly from Python.
# Mathematical operations for numeric data
abs(number) # Absolute value
avg(array_number) # Average of numeric array
ceil(number) # Ceiling of number
floor(number) # Floor of number
max(array_number) # Maximum value in numeric array
min(array_number) # Minimum value in numeric array
sum(array_number) # Sum of numeric array# Array manipulation and analysis
length(array|object|string) # Length of array, object, or string
reverse(array) # Reverse array order
sort(array) # Sort array
sort_by(array, expression) # Sort array by expression result
max_by(array, expression) # Find maximum element by expression
min_by(array, expression) # Find minimum element by expression
map(expression, array) # Apply expression to each array element# Object manipulation operations
keys(object) # Get object keys as array
values(object) # Get object values as array
merge(object...) # Merge multiple objects (variadic)# String operations and testing
contains(array|string, any) # Check if array/string contains value
ends_with(string, string) # Check if string ends with suffix
starts_with(string, string) # Check if string starts with prefix
join(string, array_string) # Join string array with separator# Type checking and conversion
type(any) # Get type of value ("string", "number", "array", "object", "boolean", "null")
not_null(any...) # Return first non-null value (variadic)
to_array(any) # Convert value to array
to_string(any) # Convert value to string
to_number(string) # Convert string to number# Mathematical functions
jmespath.search('avg(scores)', {'scores': [85, 92, 78, 96]})
# Returns: 87.75
# Array functions
jmespath.search('sort_by(users, &age)', {
'users': [{'name': 'Alice', 'age': 30}, {'name': 'Bob', 'age': 25}]
})
# Returns: [{'name': 'Bob', 'age': 25}, {'name': 'Alice', 'age': 30}]
# String functions
jmespath.search('join(`, `, names)', {'names': ['Alice', 'Bob', 'Charlie']})
# Returns: "Alice, Bob, Charlie"
# Type functions
jmespath.search('not_null(missing, backup, default)', {
'missing': None, 'backup': None, 'default': 'fallback'
})
# Returns: "fallback"import jmespath
from collections import OrderedDict
# Using custom dictionary class for ordered output
options = jmespath.Options(dict_cls=OrderedDict)
result = jmespath.search('{name: name, age: age}', user_data, options)
# Compiled expressions with options
expression = jmespath.compile('{name: name, age: age}')
result = expression.search(user_data, options)import jmespath
from jmespath.exceptions import ParseError, JMESPathTypeError
try:
result = jmespath.search('invalid..syntax', data)
except ParseError as e:
print(f"Syntax error at position {e.lex_position}: {e}")
except JMESPathTypeError as e:
print(f"Type error in {e.function_name}: {e}")
except jmespath.JMESPathError as e:
print(f"JMESPath error: {e}")JMESPath supports custom functions through subclassing. Create custom functions by extending the base Functions class.
import jmespath
from jmespath import functions
# Create a subclass of functions.Functions
class CustomFunctions(functions.Functions):
# Create methods starting with "_func_" and decorate with @signature
@functions.signature({'types': ['string']})
def _func_unique_letters(self, s):
# Given a string s, return sorted unique letters: 'ccbbadd' -> 'abcd'
return ''.join(sorted(set(s)))
@functions.signature({'types': ['number']}, {'types': ['number']})
def _func_my_add(self, x, y):
return x + y
# Use custom functions with Options
options = jmespath.Options(custom_functions=CustomFunctions())
# Apply custom functions in expressions
result = jmespath.search('my_add(`1`, `2`)', {}, options)
# Returns: 3
result = jmespath.search('foo.bar | unique_letters(@)',
{'foo': {'bar': 'ccbbadd'}}, options)
# Returns: "abcd"@functions.signature(*signature_dicts)
def _func_function_name(self, *args):
"""
Custom function implementation.
Args:
signature_dicts: Type specifications for each parameter
Format: {'types': ['string', 'number', 'array', 'object', 'boolean', 'null']}
Special: {'types': [], 'variadic': True} for variadic functions
args: Function arguments matching signature types
Returns:
any: Function result
"""__version__: str # Library version string, currently "1.0.1"
class ParsedResult:
"""Compiled JMESPath expression returned by compile()."""
expression: str # Original expression string
parsed: dict # Parsed AST (internal use)
def search(self, data, options=None):
"""
Search data using the compiled expression.
Args:
data: Python data structure to search
options (Options, optional): Options for controlling evaluation
Returns:
any: Extracted data matching the expression
"""JMESPath expressions use a JSON-specific query language with the following key syntax:
foo.bar - Access nested objectsfoo[0] - Access array elements by indexfoo[1:3] - Slice arraysfoo[*].bar - Project over arrays{name: name, id: id} - Create new objectsfoo[?bar > 10] - Filter arrays with conditionslength(foo) - Apply built-in functionsfoo | bar - Chain operations@ - Reference current node in expressionsSee JMESPath Tutorial for comprehensive language documentation.
JMESPath includes a command-line tool for interactive JSON querying.
# Search JSON from stdin
echo '{"foo": {"bar": "baz"}}' | jp.py 'foo.bar'
# Search JSON from file
jp.py 'users[*].name' -f data.json
# Display AST for debugging
jp.py 'foo.bar' --ast# jp.py command line arguments:
# expression (required): JMESPath expression to evaluate
# -f, --filename: Input JSON file (default: stdin)
# --ast: Pretty print AST instead of searching