Skip to content

Using JMESPath in APIO workflows

JMESPath is a powerful query language for extracting and transforming JSON data. In APIO, the json_query filter allows you to apply JMESPath expressions to JSON objects, enabling efficient data extraction and manipulation.

Basic Syntax and Notes

  • Applying JMESPath: Use | json_query(expression) to apply a JMESPath expression to a JSON object.
  • Output Format: To convert the result into a JSON string, use the | tojson filter after the query.
  • Powerful Queries: JMESPath supports nested queries, slicing, projections, filters, and functions.

Examples of JMESPath Queries

All these examples can be tested with the template playground

Accessing a Simple Key

Input

json
{"test": {"a": "foo", "b": "bar", "c": "baz"}}

Query

jinja
{{ test | json_query("a") }}

Result

json
foo

Accessing Nested Keys

Input

json
{"test": {"a": {"b": {"c": {"d": "value"}}}}}

Query

jinja
{{ test | json_query("a.b.c.d") }}

Result

json
value

Accessing Elements in a List

Input

json
{"test": ["a", "b", "c", "d", "e", "f"]}

Query

jinja
{{ test | json_query("[1]") }}

Result

json
b

Accessing Deeply Nested Lists

Input

json
{
    "test": {
        "a": {
            "b": {
                "c": [
                    {
                        "d": [
                            0,
                            [1, 2]
                        ]
                    },
                    {
                        "d": [
                            3,
                            4
                        ]
                    }
                ]
            }
        }
    }
}

Query

jinja
{{ test | json_query("a.b.c[0].d[1][0]") }}

Result

json
1

List Slicing

JMESPath supports Python-style slicing for lists. Input

json
{"test": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}

Queries and results

  • First 5 elements
jinja
{{ test | json_query("[0:5]") }}

Result: [0, 1, 2, 3, 4]

  • Last 5 elements
jinja
{{ test | json_query("[5:10]") }}

Result: [5, 6, 7, 8, 9]

  • Even indices
jinja
{{ test | json_query("[::2]") }}

Result: [0, 2, 4, 6, 8]

  • Reverse order
jinja
{{ test | json_query("[::-1]") }}

Result: [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

List and Sliced Projections

Input

json
{
 "test": {
  "people": [
    {"first": "James", "last": "d"},
    {"first": "Jacob", "last": "e"},
    {"first": "Jayden", "last": "f"},
    {"missing": "different"}
  ]
 }
}

Queries and results

  • Extract first names
jinja
{{ test | json_query("people[*].first") }}

Result: ['James', 'Jacob', 'Jayden']

  • Extract first names of the first two people:
jinja
{{ test | json_query("people[:2].first") }}

Result: ['James', 'Jacob']

  • Project into a custom structure:
jinja
{{ test | json_query("people[*].{firstName: first}") }}

Result: [{"firstName":"James"},{"firstName":"Jacob"},{"firstName":"Jayden"},{"firstName":null}]

Object projections

Input

json
{"test": {
  "ops": {
    "functionA": {"numArgs": 2},
    "functionB": {"numArgs": 3},
    "functionC": {"variadic": true}
  }
}}

Query

jinja
{{ test | json_query("ops.*.numArgs") }}

Result

json
[2, 3]

Flattening Nested Lists

Input

json
{
  "test": {
    "reservations": [
      {
        "instances": [
          {"state": "running"},
          {"state": "stopped"}
        ]
      },
      {
        "instances": [
          {"state": "terminated"},
          {"state": "running"}
        ]
      }
    ]
  }
}

Queries and results:

  • Extract grouped states
jinja
{{ test | json_query("reservations[].instances[*].state") }}

Result: [['running', 'stopped'], ['terminated', 'running']]

  • Flatened states
jinja
{{ test | json_query("reservations[].instances[].state") }}

Result: ['running', 'stopped', 'terminated', 'running']

Filtering Lists

Input

json
{
 "test": {
  "machines": [
    {"name": "a", "state": "running"},
    {"name": "b", "state": "stopped"},
    {"name": "b", "state": "running"}
  ]
 }
}

Query

jinja
 {{ test | json_query("machines[?state=='running'].name") }}

Result

json
['a', 'b']

Pie Expressions

MESPath supports chaining queries with pipes (|). Input

json
{
 "test": {
  "people": [
    {"first": "James", "last": "d"},
    {"first": "Jacob", "last": "e"},
    {"first": "Jayden", "last": "f"},
    {"missing": "different"}
  ]
 }
}

Query

jinja
{{ test | json_query("people[*].first | [0]") }}

Result

json
James

Multiselect Expressions

Input

json
{
 "test": {
  "people": [
    {"name": "a", "state": {"name": "up"}},
    {"name": "b", "state": {"name": "down"}},
    {"name": "c", "state": {"name": "up"}}
  ]
 }
}

Queries and results:

  • Multiselect as a list of lists:
jinja
{{ test | json_query("people[].[name, state.name]") }}

Result: [['a', 'up'], ['b', 'down'], ['c', 'up']]

  • Multiselect into objects:
jinja
{{ test | json_query("people[].{Name: name, State: state.name}") }}

Result: [{'Name': 'a', 'State': 'up'}, {'Name': 'b', 'State': 'down'}, {'Name': 'c', 'State': 'up'}]

Functions

JMESPath includes built-in functions for transformations. Input

json
{
 "test": {
  "people": [
    {"name": "b", "age": 30},
    {"name": "a", "age": 50},
    {"name": "c", "age": 40}
  ]
 }
}

Queries and results:

  • Count people:
jinja
{{ test | json_query("length(people)") }}

Result: 3

  • Find the oldest person:
jinja
{{ test | json_query("max_by(people, &age).name") }}

Result: a

  • Sum all ages:
jinja
{{ test | json_query("sum(people[].age)") }}

Result: 120

Best Practices for JMESPath in APIO

  • Use | tojson for Output: Always serialize your results for compact output.
  • Test Expressions: Validate your queries with simple JSON inputs.
  • Chain Queries: Use pipe expressions to streamline data extraction.