The JMESPath language is described in an ABNF grammar with a complete specification. This ensures that the language
syntax is precisely defined.
Compliance Test Suite
JMESPath has a full suite of data driven testcases. This ensures parity for multiple libraries, and makes it easy for
developers to implement JMESPath in their language of choice.
Libraries in Multiple Languages
Each JMESPath library passes a complete suite of compliance tests to ensure they work as intended. There are libraries
in multiple languages including python, php, javascript and lua.
Tutorial
JMESPath is a query language for JSON. You can extract and transform elements from a JSON
document. The examples below are interactive. You can change the
JMESPath expressions and see the results update automatically.
For each of these examples, the JMESPath expression is applied to the
input JSON on the left, and the result of evaluating the JMESPath
expression is shown in the JSON document on the right-hand side.
Basic Expressions
The simplest JMESPath expression is an identifier, which selects a key in an JSON object:
Try changing the expression above to b, and c and note the updated
result. Also note that if you refer to a key that does not exist, a
value of null (or the language equivalent of null) is returned.
You can use a subexpression to return to nested values in a JSON object:
If you refer to a key that does not exist, a value of null is
returned. Attempting to subsequently access identifiers will continue to
return a value of null. Try changing the expression to b.c.d.e
above.
Index Expressions allow you to select a specific element in a list. It should look similar
to array access in common programming languages. Indexing is 0 based.
If you specify an index that’s larger than the list, a value of null
is returned. You can also use negative indexing to index from the end of
the list. [-1] refers to the last element in the list, [-2] refers
to the penultimate element. Try it out in the example above.
You can combine identifiers, sub expressions, and index expressions to
access JSON elements.
Slicing
Slices allow you to select a contiguous subset of an array. If you’ve
ever used slicing in python, then you already know how to use JMESPath
slices. In its simplest form, you can specify the starting index and the
ending index. The ending index is the first index which you do not
want included in the slice. Let’s take a look at some examples. First,
given an array of integers from 0 to 9, let’s select the first half of
the array:
This slice result contains the elements 0, 1, 2, 3, and 4. The element
at index 5 is not included. If we want to select the second half of the
array, we can use this expression:
The two example above can be shortened. If the start or stop value
is omitted it is assumed to be the start or the end of the array. For
example:
Try modifying the example above to only include the last half of the
array elements without specifying the end value of 10.
The general form of a slice is [start:stop:step]. So far we’ve looked
at the [start:stop] form. By default, the step value is 1, which
means to include every element in the range specified by the start and
stop value. However, we can use the step value to skip over elements.
For example, to select only the even elements from the array.
Also note in this example we’re omitting the start as well as the
stop value, which means to use 0 for the start value, and 10 for
the stop value. In this example, the expression [::2] is equivalent
to [0:10:2].
The last thing to know about slices is that just like indexing a single
value, all the values can be negative. If the step value is negative,
then the slice is created in reverse order. For example:
The above expression creates a slice but in reverse order.
If you want all the details about how slices work, check out the
slices
in the specification.
Projections
Projections are one of the key features of JMESPath. It allows you to
apply an expression to a collection of elements. There are five kinds of
projections:
List Projections
Slice Projections
Object Projections
Flatten Projections
Filter Projections
List and Slice Projections
A wildcard expression
creates a list projection, which is a projection over a JSON array. This
is best illustrated with an example. Let’s say we have a JSON document
describing a people, and each array element is a JSON object that has a
first, last, and age key. Suppose we wanted a list of all the
first names in our list.
In the example above, the first expression, which is just an
identifier, is applied to each element in the people array. The
results are collected into a JSON array and returned as the result of
the expression. The expression can be more complex than a basic
identifier. For example, the expression foo[*].bar.baz[0] would
project the bar.baz[0] expression to each element in the foo array.
There’s a few things to keep in mind when working with projections.
These are discussed in more detail in the [wildcards] section
of the spec, but the main points are:
Projections are evaluated as two steps. The left-hand side (LHS)
creates a JSON array of initial values. The right-hand side (RHS) of
a projection is the expression to project for each element in the
JSON array created by the left-hand side. Each projection type has
slightly different semantics when evaluating either the left-hand
side and/or the right-hand side.
If the result of the expression projected onto an individual array
element is null, then that value is omitted from the collected set
of results.
You can stop a projection with a Pipe Expression (discussed later).
A list projection is only valid for a JSON array. If the value is
not a list, then the result of the expression is null.
You can try this out in the demo above. Notice how people[*].first
only included three elements, even though the people array has four
elements. This is because the last element, {"missing": "different"}
evaluates to null when the expression first is applied, and null
values are not added to the collected result array. If you try the
expression foo[*].bar you’ll see a result of null, because the
value associated with the foo key is a JSON object, not an array, and
a list projection is only defined for JSON arrays.
Slice projections are almost identical to a list projection, with the
exception that the left-hand side is the result of evaluating the slice,
which may not include all the elements in the original list:
Object Projections
Whereas a list projection is defined for a JSON array, an object
projection is defined for a JSON object. You can create an object
projection using the * syntax. This will create a list of the values
of the JSON object, and project the right-hand side of the projection
onto the list of values.
In the example above the * creates a JSON array of the values
associated with the ops JSON object. The RHS of the projection,
numArgs, is then applied to the JSON array, resulting in the final
array of [2, 3]. Below is a sample walk-through of how an
implementation could potentially implement evaluating an object
projection. First, the object projection can be broken down into its two
components, the left-hand side (LHS) and its right-hand side (RHS):
LHS: ops
RHS: numArgs
First, the LHS is evaluated to create the initial array to be projected:
Any null values are not included in the final result, so the result of
the entire expression is therefore [2, 3].
Flatten Projections
More than one projection can be used in a JMESPath expression. In the
case of a List/Object projection, the structure of the original document
is preserved when creating projection within a projection. For example,
let’s take the expression reservations[*].instances[*].state. This
expression is saying that the top level key reservations has an array
as a value. For each of those array elements, project the
instances[*].state expression. Within each list element, there’s an
instances key which itself is a value, and we create a sub projection
for each each list element in the list. Here’s an example of that:
The result of this expression is [["running", "stopped"], ["terminated", "running"]], which is a list
of lists. The outer list is from the projection of reservations[*],
and the inner list is a projection of state created from
instances[*]:
What if we just want a list of all the states of our instances? We’d
ideally like a result ["running", "stopped", "terminated", "running"].
In this situation, we don’t care which reservation the instance
belonged to, we just want a list of states.
This is the problem that a Flatten Projection solves. To
get the desired result, you can use [] instead of [*] to flatten a
list: reservations[].instances[].state. Try changing [*] to [] in
the expression above and see how the result changes.
While the flatten spec goes into more detail, a simple rule of thumb to use for the flatten operator,
[], is that:
It flattens sublists into the parent list (not recursively, just one
level).
It creates a projection, so anything on the RHS of the flatten
projection is projected onto the newly created flattened list.
You can also just use [] on its own to flatten a list:
If you flattened the result of the expression again, [][], you’d then
get a result of [0, 1, 2, 3, 4, 5, 6, 7]. Try it out in the example
above.
Filter Projections
Up to this point we’ve looked at:
List/Slice projections
Object projections
Flatten projections
Evaluating the RHS of a projection is a basic type of filter. If the
result of the expression evaluated against an individual element results
in null, then the element is excluded from the final result.
A filter projection allows you to filter the LHS of the projection
before evaluating the RHS of a projection.
For example, let’s say we have a list of machines, each has a name
and a state. We’d like the name of all machines that are running. In
pseudocode, this would be:
result = []
foreach machine in inputData['machines']
if machine['state'] == 'running'
result.insert_at_end(machine['name'])
return result
A filter projection can be used to accomplish this:
Try changing running to stopped in the example above. You can also
remove the .name at the end of the expression if you just want the
entire JSON object of each machine that has the specified state.
A filter expression is defined for an array and has the general form
LHS [? <expression> <comparator> <expression>] RHS. The
filter expression <filterexpressions>{.interpreted-text role=“ref”}
spec details exactly what comparators are available and how they work,
but the standard comparators are supported, i.e ==, !=, <, <=, >, >=.
Pipe Expressions
Projections are an important concept in JMESPath. However, there are
times when projection semantics are not what you want. A common
scenario is when you want to operate of the result of a projection
rather than projecting an expression onto each element in the array. For
example, the expression people[*].first will give you an array
containing the first names of everyone in the people array. What if you
wanted the first element in that list? If you tried people[*].first[0]
that you just evaluate first[0] for each element in the people array,
and because indexing is not defined for strings, the final result would
be an empty array, []. To accomplish the desired result, you can use a
pipe expression, <expression> | <expression>, to indicate that a
projection must stop. This is shown in the example below:
In the example above, the RHS of the list projection is first. When a
pipe is encountered, the result up to that point is passed to the RHS of
the pipe expression. The pipe expression is evaluated as:
Up to this point, we’ve looked at JMESPath expressions that help to
pare down a JSON document into just the elements you’re interested in.
This next concept,
multiselect lists
and multiselect hashes
allow you to create JSON elements. This allows you to create elements
that don’t exist in a JSON document. A multiselect list creates a list
and a multiselect hash creates a JSON object.
This is an example of a multiselect list:
In the expression above, the [name, state.name] portion is a
multiselect list. It says to create a list of two element, the first
element is the result of evaluating the name expression against the
list element, and the second element is the result of evaluating
state.name. Each list element will therefore create a two element
list, and the final result of the entire expression is a list of two
element lists.
Unlike a projection, the result of the expression in always included,
even if the result is a null. If you change the above expression to
people[].[foo, bar] each two element list will be [null, null].
A multiselect hash has the same basic idea as a multiselect list, except
it instead creates a hash instead of an array. Using the same example
above, if we instead wanted to create a two element hash that had two
keys, Name and State, we could use this:
Functions
JMESPath supports function expressions, for example:
Functions
can be used to transform and filter data in powerful ways.
Below are a few examples of functions.
This example prints the name of the oldest person in the people array:
Functions can also be combined with filter expressions. In the example
below, the JMESPath expressions finds all elements in myarray that
contains the string foo.
The @ character in the example above refers to the current element
being evaluated in myarray. The expression contains(@, `foo`)
will return true if the current element in the myarray array
contains the string foo.
While the function expression spec has all the details, there are a few things to keep in
mind when working with functions:
Function arguments have types. If an argument for a function has the
wrong type, an invalid-type error will occur. There are functions
that can do type conversions (to_string, to_number) to help get
arguments converted to their proper type.
If a function is called with the wrong number of arguments, an
invalid-arity will occur.
Next Steps
We’ve now seen an overview of the JMESPath language. The next things to
do are:
See the examples
. You’ll see common
JMESPath expressions that go beyond the tutorial. You’ll also see
you how to combine multiple features together in order to best
leverage JMESPath expressions.
To actually start using JMESPath, pick the language of your choice,
and check out the libraries
page for
more information on using JMESPath in the language of your choice.
Read the JMESPath Spec
, which
has the official ABNF grammar and full details of the semantics of
the language.
Examples
Filters and Multiselect Lists
One of the most common usage scenarios for JMESPath is being able to
take a complex JSON document and simplify it down. The main features at
work here are filters and multiselects. In this example below, we’re
taking the array of people and, for any element with an age key whose
value is greater than 20, we’re creating a sub list of the name and age
values.
Filters and Multiselect Hashes
In the previous example we were taking an array of hashes, and
simplifying down to an array of two element arrays containing a name and
an age. We’re also only including list elements where the age key is
greater than 20. If instead we want to create the same hash structure
but only include the age and name key, we can instead say:
The last half of the above expression contains key value pairs which
have the general form keyname: <expression>. In the above expression
we’re just using a field as an expression, but they can be more
advanced expressions. For example:
Notice in the above example instead of applying a filter expression
([? <expr> ]), we’re selecting all array elements via [*].
Working with Nested Data
The above example combines several JMESPath features including the
flatten
operator, multiselect lists, filters, and pipes.
The input data contains a top level key, “reservations”, which is a
list. Within each list, there is an “instances” key, which is also a
list.
The first thing we’re doing here is creating a single list from
multiple lists of instances. By using the flatten
we can take the two instances from the first list and the
two instances from the second list, and combine them into a single list.
Try changing the above expression to just reservations[].instances[]
to see what this flattened list looks like. Everything to the right of
the reservations[].instances[] is about taking the flattened list and
paring it down to contain only the data that we want. This expression is
taking each element in the original list and transforming it into a
three element sublist. The three elements are:
In the tags list, select the first element in the flattened
Values list whose Key has a value of Name.
The type
The state.name of each instance.
The most interesting of those three expressions is the
tags[?Key=='Name'].Values[] | [0] part. Let’s examine that further.
The first thing to notice is that we’re filtering down the list
associated with the tags key. The tags[?Key==`Name`] tells us to
only include list elements that contain a Key whose value is Name.
From those filtered list elements we’re going to take the Values key
and flatten the list. Finally, the | [0] will take the entire list and
extract the 0th element.
Filtering and Selecting Nested Data
In this example, we’re going to look at how you can filter nested
hashes.
In this example we’re searching through the people array. Each
element in this array contains a hash of two elements, and each value in
the hash is itself a hash. We’re trying to retrieve the value of the
general key that contains an id key with a value of 100.
If we just had the expression people[?general.id==`100`], we’d
have a result of:
Let’s walk through how we arrived at this result. In words, the
people[?general.id==`100`] expression is saying “for each element
in the people array, select the elements where the general.id equals
100”. If we trace the execution of this filtering process we have:
# First element:
{
"general": {
"id": 100,
"age": 20,
"other": "foo",
"name": "Bob"
},
"history": {
"first_login": "2014-01-01",
"last_login": "2014-01-02"
}
},
# Applying the expression ``general.id`` to this hash::
100
# Does 100==100?
true
# Add this first element (in its entirety) to the result list.
# Second element:
{
"general": {
"id": 101,
"age": 30,
"other": "bar",
"name": "Bill"
},
"history": {
"first_login": "2014-05-01",
"last_login": "2014-05-02"
}
}
# Applying the expression ``general.id`` to this element::
101
# Does 101==100?
false
# Do not add this element to the results list.
# Result of this expression is a list containing the first element.
However, this still isn’t the final value we want which is:
In order to get to this value from our filtered results we need to first
select the general key. This gives us a list of just the values of the
general hash:
From there, we then uses a pipe (|) to stop projections so that we can
finally select the first element ([0]). Note that we are making the
assumption that there’s only one hash that contains an id of 100.
Given the way the data is structured, it’s entirely possible to have
data such as:
Note here that the first and last elements in the people array both
have an id of 100. Our expression would then select the first
element that matched.
Finally, it’s worth mentioning there is more than one way to write this
expression. In this example we’ve decided that after we filter the list
we’re going to select the value of the general key and then select
the first element in that list. We could also reverse the order of those
operations, we could have taken the filtered list, selected the first
element, and then extracted the value associated with the general key.
That expression would be:
people[?general.id==`100`] | [0].general
Both versions are equally valid.
Using Functions
JMESPath functions give you a lot of power and flexibility when working with JMESPath expressions.
Below are some common expressions and functions used in JMESPath.
sort_by
The first interesting thing here if the use of the function sort_by.
In this example we are sorting the Contents array by the value of each
Date key in each element in the Contents array. The sort_by
function takes two arguments. The first argument is an array, and the
second argument describes the key that should be used to sort the array.
The second interesting thing in this expression is that the second
argument starts with &, which creates an expression type. Think of
this conceptually as a reference to an expression that can be evaluated
later. If you are familiar with lambda and anonymous functions,
expression types are similar. The reason we use &Date instead of
Date is because if the expression is Date, it would be evaluated
before calling the function, and given there’s no Date key in the
outer hash, the second argument would evaluate to null. Check out
function-evaluation{.interpreted-text role=“ref”} in the specification
for more information on how functions are evaluated in JMESPath. Also,
note that we’re taking advantage of the fact that the dates are in ISO
8601 format, which can be sorted lexicographically.
And finally, the last interesting thing in this expression is the [*]
immediately after the sort_by function call. The reason for this is
that we want to apply the multiselect hash, the second half of the
expression, to each element in the sorted array. In order to do this we
need a projection. The [*] does exactly that, it takes the input array
and creates a projection such that the multiselect hash
{Key: Key, Size: Size} will be applied to each element in the list.
There are other functions that take expression types that are similar to
sort_by including min_by
and max_by
.
Pipes
Pipe expression are useful for stopping projections. They can also be
used to group expressions.
Main Page
Let’s look at a modified version of the expression on the JMESPath front page
.
We can think of this JMESPath expression as having three components,
each separated by the pipe character |. The first expression is
familiar to us, it’s similar to the first example on this page. The
second part of the expression, sort(@), is similar to the sort_by
function we saw in the previous section. The @ token is used to refer
to the current element. The sort
function takes a single parameter which is an array. If the input JSON
document was a hash, and we wanted to sort the foo key, which was an
array, we could just use sort(foo). In this scenario, the input JSON
document is the array we want to sort. To refer to this value, we use
the current element, @, to indicate this. We’re also only taking a
subset of the sorted array. We’re using a slice ([-2:]) to indicate
that we only want the last two elements in the sorted array to be passed
through to the final third of this expression.
And finally, the third part of the expression,
{WashingtonCities: join(', ', @)}, creates a multiselect hash. It
takes as input, the list of sorted city names, and produces a hash with
a single key, WashingtonCities, whose values are the input list
(denoted by @) as a string separated by a comma.
Preview Features
Lexical Scopes
JMESPath Community introduces the let-expression
function
to supports nested lexical scopes.
let $foo = bar in {a: myvar, b: $foo}
The first argument is a JSON object that introduces a new lexical scope.
The second argument is an expression-type that is evaluated against the current context – i.e the current result of JMESPath evaluation context, that can be referred to by the @ node. The expression-type also has access to the stack of nested scopes.
Consider the following example:
When evaluating the states identifier, JMESPath no longer has access to the root scope, where first_choice is defined. Therefore, under normal circumstances, the filter-expression[?name === first_choice] would evaluate the first_choice identifier and return an empty array.
Instead, let() defined the identifier first_choice has taking the value of the property with the same name in the input JSON document. It effectively created a scope that can be represented as the following JSON object:
{ "first_choice": "WA" }
Therefore, when evaluating the filter-expression, the first_choice identifier is indeed defined, and produces the correct result.
Arithmetic Expressions
JMESPath Community now supports arithmetic-expression
syntax with the usual operators.
The items() function allows you to deconstruct a JSON object to its key and values,
whereas the from_items() function will combine two arrays into a single JSON object.
The zip() function comes the Python language. It combines two or more arrays into a set of arrays,
each of which contains the _i_th indexed item from each indivual array.
For better understanding, consider the following example:
Think of the three fruits, people and country arrays as being rows in a table.
Each array has a number of items that you can think of as being the columns in the table.
The zip() function will create as many arrays as there are full columns, each of which
will contain the items found in each row of the table.
So, the first array – corresponding to the first column of the table – will contain
one item from the people row, one item from the country row and finally one item from the fruits row,
resulting in ["John", "Germany", "Orange"].
The second array – corresponding to the second column of the table – contains ["Marc", "France", "Apple"].
String Slices
Using slice-expression to slice strings is popular in modern programming languages. JMESPath Community supports slicing strings using the same syntax:
Note: slice-expression applied to JSON arrays result in a projection. Applying a slice-expression to a JSON string, however, produces a JSON string.
Groups
Using the group_by() function
, you can group collection of objects with specific criteria:
Libraries
The JMESPath specification is implemented in various languages. Each list below shows JMESPath libraries as well as the
compliance level. The compliance level is based on which compliance tests the library can pass and as reported by their respective authors.
Fully compliant: means compliant with respect to the original version
of JMESPath.
JMESPath Community: means compliant with respect to the new JMESPath Community specifications.
Provides a JMESPath interactive terminal that you can use to evaluate JMESPath expressions as you type. The README in the github repo
shows GIFs of jpterm in action.
Provides a JMESPath command line interface called jp. This cross platform tool accepts JSON data through stdin or input files, and prints the result of evaluating the JMESPath expression to stdout. This is useful if you're writing shell scripts that need to manipulate JSON data.
Syntax
In this specification, examples are shown through the use of a search function. The syntax for this function is:
For simplicity, the jmespath expression and the JSON document are not quoted. For example:
search(foo, {"foo": "bar"}) -> "bar"
The result of applying a JMESPath expression against a JSON document will always result in valid JSON, provided there
are no errors during the evaluation process. Structured data in, structured data out.
This also means that, with the exception of JMESPath expression types, JMESPath only supports the same types supported by JSON:
number (integers and double-precision floating-point format in JSON)
string (a sequence of Unicode code points
. Note that a code point is distinct to a code unit
)
boolean (true or false)
array (an ordered, sequence of values)
object (an unordered collection of key value pairs)
Implementations can map the corresponding JSON types to their language equivalent. For example, a JSON null could map to
None in python, and nil in ruby and go.
Errors
Errors may be raised during the JMEspath evaluation process. How and when errors are raised is implementation specific,
but implementations should indicate to the caller when errors have occurred.
The following errors are defined:
invalid-arity is raised when an invalid number of function arguments is encountered during the evaluation process.
invalid-type is raised when an invalid type is encountered during the evaluation process.
invalid-value is raised when an invalid value is encountered during the evaluation process.
not-a-number is raised when arithmetic expressions overflow.
unknown-function is raised when an unknown function is encountered during the evaluation process.
Grammar
JMESPath grammar is specified using ABNF, as described in RFC4234
In addition to the grammar, there is the following token precedence that goes from weakest to tightest binding:
pipe: |
or: ||
and: &&
unary not: !
rbracket: ]
expression=sub-expression/index-expression/comparator-expressionexpression=/or-expression/identifierexpression=/and-expression/not-expression/paren-expressionexpression=/multi-select-list/multi-select-hash/literalexpression=/function-expression/pipe-expression/raw-stringexpression=/root-node/current-nodeexpression=/arithmetic-expressionexpression=/let-expression/variable-refsub-expression=expression "." ( identifier/multi-select-list/multi-select-hash/function-expression/ "*" )
pipe-expression=expression "|" expressionor-expression=expression "||" expressionand-expression=expression "&&" expressionnot-expression= "!" expressionarithmetic-expression=/ "+" expression; + %x43 arithmetic-expression=/ ( "-" / "–" ) expression; - %x45 – %x2212 \arithmetic-expression=expression "%" expression; % %x37 \arithmetic-expression=/expression ( "*" / "×" ) expression; * %x42 × %xD7 \arithmetic-expression=/expression "+" expression; + %x43 \arithmetic-expression=/expression ( "-" / "–" ) expression; - %x45 – %x2212 \arithmetic-expression=/expression ( "/" / "÷" ) expression; / %x47 ÷ %F7 \arithmetic-expression=expression "//" expression; // %47 %47paren-expression= "(" expression ")"
index-expression=expressionbracket-specifier/bracket-specifierbracket-specifier= "[" (number/slice-expression) "]"
bracket-specifier=/ "[]"
slice-expression= [number] ":" [number] [ ":" [number] ]
multi-select-list= "[" ( expression*( "," expression ) ) "]"
multi-select-hash= "{" ( keyval-expr*( "," keyval-expr ) ) "}"
keyval-expr=identifier ":" expressionexpression=/ "*"
bracket-specifier=/ "[" "*" "]"
filter-expression= "[?" expression "]"
bracket-specifier=/filter-expressioncomparator-expression=expressioncomparatorexpressioncomparator= "<" / "<=" / "==" / ">=" / ">" / "!="
function-expression=unquoted-string ( no-args/one-or-more-args )
no-args= "(" ")"
one-or-more-args= "(" ( function-arg*( "," function-arg ) ) ")"
function-arg=expression/expression-typecurrent-node= "@"
root-node= "$"
expression-type= "&" expressionlet-expression= "let" bindings "in" expressionbindings=variable-binding*( "," variable-binding ) \
variable-binding=variable-ref "=" expression \
variable-ref= "$" unquoted-stringraw-string= "'" *raw-string-char "'"
raw-string-char= (%x00-26 / %x28-5B / %x5D-10FFFF) /preserved-escape/raw-string-escapepreserved-escape=escape (%x00-26 / %x28-5B / %x5D-10FFFF)
raw-string-escape=escape ("'" /escape)
literal= "`" json-text "`"
number= ["-"] 1*digitdigit= %x30-39 ; 0-9identifier=unquoted-string/quoted-stringunquoted-string= (%x41-5A / %x61-7A / %x5F) *( ; A-Za-z_ %x30-39 /; 0-9 %x41-5A /; A-Z %x5F /; _ %x61-7A) ; a-zquoted-string=quotation-mark*(unescaped-char/escaped-char) quotation-markunescaped-char= %x20-21 / %x23-5B / %x5D-10FFFF
escape= %x5C ; \quotation-mark= %x22 ; "escaped-char=escape (
%x22 /; " quotation mark U+0022 %x5C /; \ reverse solidus U+005C %x2F /; / solidus U+002F %x62 /; b backspace U+0008 %x66 /; f form feed U+000C %x6E /; n line feed U+000A %x72 /; r carriage return U+000D %x74 /; t tab U+0009 %x75 4HEXDIG ) ; uXXXX U+XXXXjson-text=wsjson-valuewsws=*(
%x20 /; Space %x09 /; Horizontal tab %x0A /; Line feed or New line %x0D ) ; Carriage return; `json-value` is any valid JSON value with the one exception that each; U+0060 GRAVE ACCENT '`' must be escaped with a preceding backslash.; While implementations are encouraged to use any existing JSON parser for this; section of the grammar (after handling the escaped characters), a complete; set of rules derived from RFC 8259 is included below:json-value=false/null/true/json-object/json-array/json-number/json-string; JSON literalsfalse= %x66.61.6c.73.65 ; falsenull= %x6e.75.6c.6c ; nulltrue= %x74.72.75.65 ; true; JSON stringsjson-string=quotation-mark*( json-unescaped/json-escaped ) quotation-markjson-unescaped= %x20-21 /; space or '!' (precedes U+0022 '"') %x23-5B /; '#' through '[' (precedes U+005C '\') %x5D-5F /; ']' through '_' (precedes U+0060 '`') %x61-10FFFF ; 'a' and all following code pointsjson-escaped=escaped-char/ (escape "`")
; JSON arraysjson-array=begin-array [ json-value*( value-separatorjson-value ) ] end-arraybegin-array=ws %x5B ws; [ left square bracketend-array=ws %x5D ws; ] right square bracketvalue-separator=ws %x2C ws; , comma; JSON objectsjson-object=begin-object [ member*( value-separatormember ) ] end-objectbegin-object=ws %x7B ws; { left curly bracketend-object=ws %x7D ws; } right curly bracketmember=json-stringname-separatorjson-valuename-separator=ws %x3A ws; : colon; JSON numbersjson-number= [ minus ] int [ frac ] [ exp ]
decimal-point= %x2E ; .digit1-9= %x31-39 ; 1-9e = %x65 / %x45 ; e Eexp= e [ minus/plus ] 1*digitfrac=decimal-point1*digitint=zero/ ( digit1-9*digit )
minus= %x2D ; -plus= %x2B ; +zero= %x30 ; 0
A sub-expression is a combination of two expressions separated by the ‘.’ char. A sub-expression is evaluated as follows:
Evaluate the expression on the left with the original JSON document.
Evaluate the expression on the right with the result of the left expression evaluation.
In pseudocode:
left-evaluation = search(left-expression, original-json-document)
if left-evaluation is `null` then result = `null`
else result = search(right-expression, left-evaluation)
A sub-expression is itself an expression, so there can be multiple levels of sub-expressions: grandparent.parent.child.
Examples
Given a JSON document: {"foo": {"bar": "baz"}}, and a jmespath expression: foo.bar, the evaluation process would be:
A pipe expression combines two expressions, separated by the | character.
It is similar to a sub-expression with a few important distinctions:
Any expression can be used on the right hand side. A sub-expression restricts the type of expression that can be
used on the right hand side.
A pipe-expression stops projections on the left hand side for propagating to the right hand side. If the left
expression creates a projection, it does not apply to the right hand side.
Contrary to a sub-expression, a pipe-expression does not stop evaluation if the left-hand-side evaluates to null.
In pseudocode:
left-evaluation = search(left-expression, original-json-document)
result = search(right-expression, left-evaluation)
The first part of the expression, foo[*], creates a projection. At this point, the remaining expression, bar is
projected onto each element of the list created from foo[*]. If you project the [0] expression, you will get the
first element from each sub list. The expression foo[*].bar[0] will return:
["first1", "first2"]
If you instead wanted only the first sub list, ["first1", "second1"], you can use a pipe-expression:
An or expression will evaluate to either the left expression or the right expression. If the evaluation of the left
expression is not false it is used as the return value. If the evaluation of the right expression is not false it is
used as the return value. If neither the left or right expression are non-null, then a value of null is returned. A
false value corresponds to any of the following conditions:
Empty list: []
Empty object: {}
Empty string: ""
False boolean: false
Null value: null
A true value corresponds to any value that is not false.
An and expression will evaluate to either the left expression or the right expression. If the expression on the left
hand side is a truth-like value, then the value on the right hand side is returned. Otherwise the result of the
expression on the left hand side is returned. This also reduces to the expected truth table:
Truth table for and expressions
LHS
RHS
Result
True
True
True
True
False
False
False
True
False
False
False
False
This is the standard truth table for a logical conjunction (AND).
A not-expression negates the result of an expression. If the expression results in a truth-like value, a not-expression
will change this value to false. If the expression results in a false-like value, a not-expression will change this value
to true.
In the absence of parentheses, operators of the same level of precedence are evaluated from left to right.
Arithmetic operators have higher precedence than comparison operators and lower precedence than the . “dot” sub-expression token separator.
An index expression is used to access elements in a list. Indexing is 0 based, the index of 0 refers to the first
element of the list. A negative number is a valid index. A negative number indicates that indexing is relative to the
end of the list, specifically:
negative-index == (length of array) + negative-index
Given an array of length N, an index of -1 would be equal to a positive index of N - 1, which is the last element of
the list. If an index expression refers to an index that is greater than the length of the array, a value of null is returned.
For the grammar rule expression bracket-specifier the expression is first evaluated, and then return value from the
expression is given as input to the bracket-specifier.
Using a “*” character within a bracket-specifier is discussed below in the wildcard expressions section.
Flatten Operator
bracket-specifier =/ "[]"
When the character sequence [] is provided as a bracket specifier, then a flattening operation occurs on the current
result. The flattening operator will merge sublists in the current result into a single list. The flattening operator
has the following semantics:
Create an empty result list.
Iterate over the elements of the current result.
If the current element is not a list, add to the end of the result list.
If the current element is a list, add each element of the current element to the end of the result list.
The result list is now the new current result.
Once the flattening operation has been performed, subsequent operations are projected onto the flattened list with the
same semantics as a wildcard expression. Thus the difference between [*] and [] is that [] will first flatten sublists
in the current result.
A slice expression allows you to select a subset of an array or a string. A slice has a start, stop, and step value. The
general form of a slice is [start:stop:step], but each component is optional and can be omitted.
Given a start, stop, and step value, the sub elements in an array or characters in a string are extracted as follows:
The first element in the extracted array or first character in the extracted string is the index denoted by start.
The last element in the extracted array or last character in the extracted string is the index denoted by end - 1.
The step value determines how many indices to skip after each element is selected from the array or each character is selected from the string.
The default step value of 1 will not skip any indices and will return a contiguous subset of the original array or a substring of the original string.
A step value greater than 1 will skip indices while extracting elements from an array or characters from a string. For instance, a step value of 2 will
skip every other element or character.
Negative step values start from the end of the array or string and extract elements or characters in reverse order.
Slice expressions adhere to the following rules:
If a negative start position is given, it is calculated as the total length of the array or string plus the given start position.
If no start position is given, it is assumed to be 0 if the given step is greater than 0 or the end of the array or string if
the given step is less than 0.
If a negative stop position is given, it is calculated as the total length of the array or string plus the given stop position.
If no stop position is given, it is assumed to be the length of the array or string if the given step is greater than 0 or 0 if
the given step is less than 0.
If the given step is omitted, it it assumed to be 1.
If the given step is 0, an invalid-value error MUST be raised.
If the element being sliced is not an array or a string, the result is null.
If the element being sliced is an array or string and yields no results, the result MUST be an empty array.
A multiselect expression is used to extract a subset of elements from a JSON hash. There are two version of multiselect,
one in which the multiselect expression is enclosed in {...} and one which is enclosed in [...]. This section describes
the [...] version. Within the start and closing characters is one or more non expressions separated by a comma. Each
expression will be evaluated against the JSON document. Each returned element will be the result of evaluating the
expression. A multi-select-list with N expressions will result in a list of length N. Given a multiselect expression
[expr-1,expr-2,...,expr-n], the evaluated expression will return [evaluate(expr-1), evaluate(expr-2), ..., evaluate(expr-n)].
A multi-select-hash expression is similar to a multi-select-list expression, except that a hash is created instead of a
list. A multi-select-hash expression also requires key names to be provided, as specified in the keyval-expr rule.
Given the following rule:
keyval-expr = identifier ":" expression
The identifier is used as the key name and the result of evaluating the expression is the value associated with the identifier key.
Each keyval-expr within the multi-select-hash will correspond to a single key value pair in the created hash.
Examples
Given a multi-select-hash expression {foo: one.two, bar: bar} and the data {"bar": "bar", {"one": {"two": "one-two"}}},
the expression is evaluated as follows:
A hash is created: {}
A key foo is created whose value is the result of evaluating one.two against the provided JSON document: {"foo": evaluate(one.two, <data>)}
A key bar is created whose value is the result of evaluting the expression bar against the provided JSON document.
The final result will be: {"foo": "one-two", "bar": "bar"}.
A wildcard expression is a expression of either * or [*]. A wildcard expression can return multiple elements, and the
remaining expressions are evaluated against each returned element from a wildcard expression. The [*] syntax applies to
a list type and the *syntax applies to a hash type.
The [*] syntax (referred to as a list wildcard expression) will return all the elements in a list. Any subsequent
expressions will be evaluated against each individual element. Given an expression [*].child-expr, and a list of N
elements, the evaluation of this expression would be [child-expr(el-0), child-expr(el-2), ..., child-expr(el-N)].
This is referred to as a projection, and the child-expr expression is projected onto the elements of the resulting list.
Once a projection has been created, all subsequent expressions are projected onto the resulting list.
The * syntax (referred to as a hash wildcard expression) will return a list of the hash element’s values. Any subsequent
expression will be evaluated against each individual element in the list (this is also referred to as a projection).
Note that if any subsequent expression after a wildcard expression returns a null value, it is omitted from the final
result list.
A list wildcard expression is only valid for the JSON array type. If a list wildcard expression is applied to any other
JSON type, a value of null is returned.
Similarly, a hash wildcard expression is only valid for the JSON object type. If a hash wildcard expression is applied
to any other JSON type, a value of null is returned. Note that JSON hashes are explicitly defined as unordered.
Therefore a hash wildcard expression can return the values associated with the hash in any order. Implementations
are not required to return the hash values in any specific order.
A filter expression provides a way to select JSON elements based on a comparison to another expression. A filter
expression is evaluated as follows: for each element in an array evaluate the expression against the element. If the
expression evaluates to a truth-like value, the item (in its entirety) is added to the result list. Otherwise it is
excluded from the result list. A filter expression is only defined for a JSON array. Attempting to evaluate a filter
expression against any other type will return null.
Comparison Operators
The following operations are supported:
==, tests for equality.
!=, tests for inequality.
<, less than.
<=, less than or equal to.
, greater than.
=, greater than or equal to.
The behavior of each operation is dependent on the type of each evaluated expression.
The comparison semantics for each operator are defined below based on the corresponding JSON type:
Equality Operators
For string/number/true/false/null types, equality is an exact match. A string is equal to another string if
they they have the exact sequence of code points. The literal values true/false/null are only equal to their own literal
values. Two JSON objects are equal if they have the same set of keys and values (given two JSON objects x and y, for each
key value pair (i, j) in x, there exists an equivalent pair (i, j) in y). Two JSON arrays are equal if they have equal
elements in the same order (given two arrays x and y, for each i from 0 until length(x), x[i] == y[i]).
Ordering Operators
Ordering operators >, >=, <, <= are only valid for numbers. Evaluating any other type with a comparison operator
will yield a null value, which will result in the element being excluded from the result list. For example, given:
The three elements in the foo list are evaluated against a < b. The first element resolves to the comparison “char” <
“bar”, and because these types are string, the expression results in null, so the first element is not included in the
result list. The second element resolves to 2 < 1, which is false, so the second element is excluded from the result
list. The third expression resolves to 1 < 2 which evaluates to true, so the third element is included in the list.
The final result of that expression is [{"a": 1, "b": 2}].
Functions allow users to easily transform and filter data in JMESPath expressions.
Data Types
In order to support functions, a type system is needed. The JSON types are used:
number (integers and double-precision floating-point format in JSON)
string (a sequence of Unicode code points
. Note that a code point is distinct to a code unit
)
boolean (true or false)
array (an ordered, sequence of values)
object (an unordered collection of key value pairs)
null
There is also an additional type that is not a JSON type that’s used in JMESPath functions:
expression (denoted by &expression)
current-node
The current-node token can be used to represent the current node being evaluated. The current-node token is useful
for functions that require the current node being evaluated as an argument. For example, the following expression
creates an array containing the total number of elements in the foo object followed by the value of foo["bar"].
foo[].[count(@), bar]
JMESPath assumes that all function arguments operate on the current node unless the argument is a literal or number
token. Because of this, an expression such as @.bar would be equivalent to just bar, so the current node is only
allowed as a bare expression.
current-node state
At the start of an expression, the value of the current node is the data being evaluated by the JMESPath expression.
As an expression is evaluated, the value the the current node represents MUST change to reflect the node currently
being evaluated. When in a projection, the current node value must be changed to the node currently being evaluated
by the projection.
root-node
The root-node token can be used to represent the original input JSON document.
As a JMESPath expression is being evaluated, the current scope changes.
Given a simple sub expression such as foo.bar, first the foo expression is evaluated
with the starting input JSON document, and the result of that expression is then used as
the current scope when the bar element is evaluated.
Once we’ve drilled down to a specific scope, the root-node token can be used
to refer to the original JSON document.
A let-expression introduces lexical scoping and lets you bind variables that are evaluated In
the context of a given lexical scope. This enables queries that can refer to elements defined
outside of their current element
Examples of this new syntax:
let $foo = bar in {a: myvar, b: $foo}
let $foo = baz[0] in bar[? baz == $foo ] | [0]
let $a = b, $c = d in bar[*].[$a, $c, foo, bar]
Function Evaluation
Functions are evaluated in applicative order. Each argument must be an expression, each argument expression must be
evaluated before evaluating the function. The function is then called with the evaluated function arguments. The result
of the function-expression is the result returned by the function call. If a function-expression is evaluated for a
function that does not exist, the JMESPath implementation must indicate to the caller that an unknown-function error
occurred. How and when this error is raised is implementation specific, but implementations should indicate to the caller
that this specific error occurred.
Functions can have a specific arity, a range of valid – minimum and maximum – number of arguments or be variadic with a
minimum number of arguments. If a function-expression is encountered where the arity does not match or the minimum number
of arguments for a variadic function is not provided, then implementations must indicate to the caller that an invalid-arity
error occurred. How and when this error is raised is implementation specific.
Each function signature declares the types of its input parameters. If any type constraints are not met, implementations
must indicate that an invalid-type error occurred.
In order to accommodate type constraints, functions are provided to convert types to other types (to_string, to_number)
which are defined below. No explicit type conversion happens unless a user specifically uses one of these type conversion
functions.
Function expressions are also allowed as the child element of a sub expression. This allows functions to be used with
projections, which can enable functions to be applied to every element in a projection. For example, given the input
data of ["1", "2", "3", "notanumber", true], the following expression can be used to convert (and filter) all elements
to numbers:
A raw string is an expression that allows for a literal string value to be specified. The result of evaluating the raw
string literal expression is the literal string value. It is a simpler form of a literal expression that is special
cased for strings. For example, the following expressions both evaluate to the same value: “foo”:
A literal expression is an expression that allows arbitrary JSON objects to be specified. This is useful in filter
expressions as well as multi select hashes (to create arbitrary key value pairs), but is allowed anywhere an expression
is allowed. The specification includes the ABNF for JSON, implementations should use an existing JSON parser to parse
literal values. Note that the ` character must now be escaped in a json-value which means implementations need to
handle this case before passing the resulting string to a JSON parser.
An identifier is the most basic expression and can be used to extract a single element from a JSON document.
The return value for an identifier is the value associated with the identifier. If the identifier does not exist in the
JSON document, than a null value is returned.
From the grammar rule listed above identifiers can be one or more characters, and must start with A-Za-z_.
An identifier can also be quoted. This is necessary when an identifier has characters not specified in the
unquoted-string grammar rule. In this situation, an identifier is specified with a double quote, followed by any number
of unescaped-char or escaped-char characters, followed by a double quote. The quoted-string rule is the same grammar
rule as a JSON string, so any valid string can be used between double quoted, include JSON supported escape sequences,
and six character unicode escape sequences.
Note that any identifier that does not start with A-Za-z_ must be quoted.