Preview - jmespath-community/jmespath.spec GitHub Wiki

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:

let $first_choice = first_choice in states[?name == $first_choice].cities[]
{"first_choice": "WA",
 "states": [
   {"name": "WA", "cities": ["Seattle", "Bellevue", "Olympia"]},
   {"name": "CA", "cities": ["Los Angeles", "San Francisco"]},
   {"name": "NY", "cities": ["New York City", "Albany"]}
 ]
}

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.

{ a: a, b: b, c: b × `2` } | a ÷ ( b − c)
{
  "a": 40,
  "b": 2
}

Object Manipulation Functions

As a JSON query language, JMESPath Community now supports functions to manipulate JSON objects.

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.

from_items(items(@))
{
  "foo": "lorem",
  "bar": "ipsum",
  "baz": "dolor"
}

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:

zip(people, country, fruits)
{
  "fruits": ["Orange", "Apple", "Strawberry"],
  "people": ["John", "Marc", "Paul"],
  "country": ["Germany", "France", "USA" ]
}

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:

foo[:4]
{
  "foo": "Hello, world!",
  "bar": ["h", "e", "l", "l", "o", " ", "w", "o", "r", "l", "d", "!"]
}

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:

group_by(items, &spec.nodeName)
{
  "items": [
    { "spec": { "nodeName": "node_01", "other": "values_01" } },
    { "spec": { "nodeName": "node_02", "other": "values_02" } },
    { "spec": { "nodeName": "node_03", "other": "values_03" } },
    { "spec": { "nodeName": "node_01", "other": "values_04" } }
  ]
}