Skip to content

jmespath

"JMESPath is a query language for JSON." - http://jmespath.org

Quotes matter

Something that is completely absent from the jmespath tutorial is that quotes matter. You must quote value strings with single quotes or back-ticks, which means you have to quote the python function args with double quotes, use back-ticks, or escape your single quotes:

>>> print(l)
[{"name": "foo-name", "age": "foo-age"}, {"name": "bar-name", "age": "bar-age"}]
>>> print(yaml.dump(l))
- age: foo-age
  name: foo-name
- age: bar-age
  name: bar-name

>>> jmespath.search("[?name == 'bar-name']", l)
[{'name': 'bar-name', 'age': 'bar-age'}]
>>> jmespath.search('[?name == "bar-name"]', l)
[]
>>> jmespath.search('[?name == \'bar-name\']', l)
[{'name': 'bar-name', 'age': 'bar-age'}]
>>> jmespath.search("[?name == `bar-name`]", l)
[{'name': 'bar-name', 'the.age': 'bar-the.age'}]

However, in jmespath you also must double-quote keys (variable names) that contain dots. (Double-quotes are optional for keys that do not contain dots.) This becomes a bit burdensome to keep track of, and also failure-prone:

>>> l = [{"name": "foo-name", "the.age": "foo-the.age"}, {"name": "bar-name", "the.age": "bar-the.age"}]
>>> l[1]['the.age']
'bar-the.age'
>>> jmespath.search("[?'the.age' == 'bar-the.age']", l)
[]
>>> jmespath.search('[?"the.age" == "bar-the.age"]', l)
[]
>>> jmespath.search('[?"the.age" == \'bar-the.age\']', l)
[{'name': 'bar-name', 'the.age': 'bar-the.age'}]
>>> jmespath.search('[?"the.age" == `bar-the.age`]', l)
[{'name': 'bar-name', 'the.age': 'bar-the.age'}]
>>> jmespath.search("[?\"the.age\" == 'bar-the.age']", l)
[{'name': 'bar-name', 'the.age': 'bar-the.age'}]

Triple quotes can help here by avoiding the need to escape both single-quotes and double-quotes:

>>> jmespath.search("""[?'the.age' == 'bar-the.age']""", l)
[]
>>> jmespath.search("""[?"the.age" == "bar-the.age"]""", l)
[]
>>> jmespath.search("""[?"the.age" == 'bar-the.age']""", l)
[{'name': 'bar-name', 'the.age': 'bar-the.age'}]
>>> jmespath.search("""[?"the.age" == `bar-the.age`]""", l)
[{'name': 'bar-name', 'the.age': 'bar-the.age'}]

The TL;DR is to only double-quote variable names, and single-quote or back-tick-quote anything that is a string literal. This requirement is inconsistent with normal python comparisons. The behavior is strict and unintuitive to people unfamiliar with jmespath. The fact that the failures are silent is quite problematic and leads to low confidence that jmespath is behaving as expected. jmespath would do well to have a linter to warn about this behavior, and at least mention the different types of quotes and their behaviors in the tutorial. (FWIW, these details are buried in the jmespath spec, including the nuanced differences between single-quotes and back-ticks.)

Examples

Grab some kubernetes fields and remap them to be less deep

kubectl get po --all-namespaces -o json |
jp "items[*].{name: metadata.name, namespace: metadata.namespace, imagePullSecrets: spec.imagePullSecrets[*].name}"

Or filter only to non-default namespace where imagePullSecrets is populated

kubectl get po --all-namespaces -o json |
jp "items[?metadata.namespace != 'default' && spec.imagePullSecrets != null].{name: metadata.name, namespace: metadata.namespace, imagePullSecrets: spec.imagePullSecrets[*].name}"