Skip to main content

A jsonpath implementation powered by treepath technology.

Project description

The jsonpath_tp Package.

The jsonpath_tp is a jsonpath implementation built on top of treepath technology. Jsonpath is query language for extracting data from json document. The jsonpath_tp attempts to follow the standard defined in jsonpath with the exceptions:

  • script expression are not supported
  • filter (script) expression support any single argument python function.
  • filter expression support regular expression

The jsonpath_tp can be used programmatically or via th OS command line interface (CLI) with the addition of jsonpath_cli with package.

Quick Start Programmatically

All the jsonpath_tp components should be imported as follows:

from jsonpath_tp import get, find

A jsonpath example that gets c's value from json data.

data = {
    "a": {
        "b": [
            {
                "c": 1
            },
            {
                "c": 2
            }]
    }
}
value = get("$.a.b[0].c", data)
assert value == 1

A jsonpath example that gets c's value from json data.

value = [value for value in find("$.a.b[*].c", data)]
assert value == [1, 2]

Solar System Json Document

The examples shown in this README use the following json document. It describes our solar system. Click to expand.

solar_system = {...}

{
  "star": {
    "name": "Sun",
    "diameter": 1391016,
    "age": null,
    "planets": {
      "inner": [
        {
          "name": "Mercury",
          "Number of Moons": "0",
          "diameter": 4879,
          "has-moons": false
        },
        {
          "name": "Venus",
          "Number of Moons": "0",
          "diameter": 12104,
          "has-moons": false
        },
        {
          "name": "Earth",
          "Number of Moons": "1",
          "diameter": 12756,
          "has-moons": true
        },
        {
          "name": "Mars",
          "Number of Moons": "2",
          "diameter": 6792,
          "has-moons": true
        }
      ],
      "outer": [
        {
          "name": "Jupiter",
          "Number of Moons": "79",
          "diameter": 142984,
          "has-moons": true
        },
        {
          "name": "Saturn",
          "Number of Moons": "82",
          "diameter": 120536,
          "has-moons": true
        },
        {
          "name": "Uranus",
          "Number of Moons": "27",
          "diameter": 51118,
          "has-moons": true
        },
        {
          "name": "Neptune",
          "Number of Moons": "14",
          "diameter": 49528,
          "has-moons": true
        }
      ]
    }
  }
}

query examples.

Description Xpath jsonpath treepath
Find planet earth. /star/planets/inner[name='Earth'] $.star.planets.inner[?(@.name=='Earth')] path.star.planets.inner[*][?(@.name == 'Earth')]
List the names of all inner planets. /star/planets/inner[*].name $.star.planets.inner[*].name path.star.planets.inner[*].name
List the names of all planets. /star/planets/*/name $.star.planets.[].name path.star.planets.wc[*].name
List the names of all celestial bodies //name $..name path.rec.name
List all nodes in the tree Preorder //* $.. path.rec
Get the third rock from the sun /star/planets/inner[3] $.star.planets.inner[2] path.star.planets.inner[2]
List first two inner planets /star/planets.inner[position()<3] $.star.planets.inner[:2] path.star.planets.inner[0:2]
$.star.planets.inner[0, 1] path.star.planets.inner[0, 2]
List planets smaller than earth /star/planets/inner[diameter < 1] $.star.planets.inner[?(@.diameter < 12756)] path.star.planets.inner[wc][has(path.diameter < 12756)]
List celestial bodies that have planets. //*[planets]/name $..[?(@.planets)].name path.rec[?(@.planets)].name
List the planets with more than 50 moons $..[?(int(@['Number of Moons']) > 50)].name path.rec[wc][has(path['Number of Moons'] > 50, int)].name

Traversal Functions

get

The get function returns the first value the path leads to.

Get the star name from the solar_system

sun = get("$.star.name", solar_system)
assert sun == 'Sun'

When there is no match, MatchNotFoundError is thrown.

try:
    get("$.star.human_population", solar_system)
    assert False, "Not expecting humans on the sun"
except MatchNotFoundError:
    pass

Or if preferred, a default value can be given.

human_population = get("$.star.human_population", solar_system, default=0)
assert human_population == 0

In addition to a constant, the default value may also be a callable

def population():
    return 0

human_population = get("$.star.human_population", solar_system, default=population)
assert human_population == 0

The default value can be automatically injected in to json document

human_population = get("$.star.human_population", solar_system, default=1, store_default=True)
assert human_population == solar_system['star']["human_population"]

find

The find function returns an Iterator that iterates to each value the path leads to. Each value is determine on its iteration.

Find All the planet names.

inner_planets = [planet for planet in find("$.star.planets.inner[*].name", solar_system)]
assert inner_planets == ['Mercury', 'Venus', 'Earth', 'Mars']

Tracing Debugging

All the functions: get, find, support tracing. An option, when enabled, records the route the algorithm takes to determine a match.

This example logs the route the algorithm takes to find the inner planets. The print function is give to capture the logs, but any single argument function can be used.

inner_planets = [planet for planet in find("$.star.planets.inner[*].name", solar_system, trace=log_to(print))]
assert inner_planets == ['Mercury', 'Venus', 'Earth', 'Mars']

The results

"""
at $.star got {'name': 'Sun', 'dia...
at $.star.planets got {'inner': [{'name': ...
at $.star.planets.inner got [{'name': 'Mercury',...
at $.star.planets.inner[*] got {'name': 'Mercury', ...
at $.star.planets.inner[0].name got 'Mercury'
at $.star.planets.inner[*] got {'name': 'Venus', 'N...
at $.star.planets.inner[1].name got 'Venus'
at $.star.planets.inner[*] got {'name': 'Earth', 'N...
at $.star.planets.inner[2].name got 'Earth'
at $.star.planets.inner[*] got {'name': 'Mars', 'Nu...
at $.star.planets.inner[3].name got 'Mars'
"""

Path

The root

The $ point to root of the tree.

value = get("$", solar_system)

assert value == solar_system

In a filter, the @ point to the current element.

value = get("$.star[?(@ == 'Sun')]", solar_system)

assert value == 'Sun'

Dictionaries

Keys

The dictionary keys are referenced as dynamic attributes on a path.

inner_from_attribute = get("$.star.planets.inner", solar_system)
inner_from_string_keys = get("$.['star']['planets']['inner']", solar_system)

assert inner_from_attribute == inner_from_string_keys == solar_system['star']['planets']['inner']

Keys With Special Characters

Dictionary keys that are not valid python syntax can be referenced as quoted strings.

sun_equatorial_diameter = get("$.star.planets.inner[0]['Number of Moons']", solar_system)

assert sun_equatorial_diameter == solar_system['star']['planets']['inner'][0]['Number of Moons']

Wildcard as a Key.

The * attribute specifies all sibling keys. It is useful for iterating over attributes.

star_children = [child for child in find("$.star.*", solar_system)]
assert star_children == [solar_system['star']['name'],
                         solar_system['star']['diameter'],
                         solar_system['star']["age"],
                         solar_system['star']['planets'], ]

Comma Delimited Keys

Multiple dictionary keys can be specified using a comma delimited list.

last_and_first = [planet for planet in find("$.star['diameter', 'name']", solar_system)]
assert last_and_first == [1391016, "Sun"]

List

Indexes

List can be access using index.

earth = get("$.star.planets.inner[2]", solar_system)
assert earth == solar_system['star']['planets']['inner'][2]

List the third inner and outer planet.

last_two = [planet for planet in find("$.star.*.*[2].name", solar_system)]
assert last_two == ['Earth', 'Uranus']

Comma Delimited Indexes.

List indexes can be specified as a comma delimited list.

last_and_first = [planet for planet in find("$.star.planets.outer[3, 0].name", solar_system)]
assert last_and_first == ["Neptune", "Jupiter"]

Slices

List can be access using slices.

List the first two planets.

first_two = [planet for planet in find("$.star.planets.outer[:2].name", solar_system)]
assert first_two == ["Jupiter", "Saturn"]

List the last two planets.

last_two = [planet for planet in find("$.star.planets.outer[-2:].name", solar_system)]
assert last_two == ["Uranus", "Neptune"]

List all outer planets in reverse.

last_two = [planet for planet in find("$.star.planets.outer[::-1].name", solar_system)]
assert last_two == ["Neptune", "Uranus", "Saturn", "Jupiter"]

List the last inner and outer planets.

last_two = [planet for planet in find("$.star.*.*[-1:].name", solar_system)]
assert last_two == ["Mars", "Neptune"]

Wildcard as an Index.

The * token can be used as a list index. It is useful for iterating over attributes.

all_outer = [planet for planet in find("$.star.planets.outer[*].name", solar_system)]
assert all_outer == ["Jupiter", "Saturn", "Uranus", "Neptune"]

Recursion

The .. double dot implies recursive search. It executes a preorder tree traversal. The search algorithm descends the tree hierarchy evaluating the path on each vertex until a match occurs. On each iteration it continues where it left off. This is an example that finds all the planets names.

all_planets = [p for p in find("$.star.planets..name", solar_system)]
assert all_planets == ['Mercury', 'Venus', 'Earth', 'Mars', 'Jupiter', 'Saturn', 'Uranus', 'Neptune']

Here is another example that finds all the celestial bodies names.

all_celestial_bodies = [p for p in find("$..name", solar_system)]
assert all_celestial_bodies == ['Sun', 'Mercury', 'Venus', 'Earth', 'Mars', 'Jupiter', 'Saturn', 'Uranus',
                                'Neptune']

Filters

Filters are use to add additional search criteria.

filter

The ?() is a filter that evaluates a branched off path relative to its parent path. This example finds all celestial bodies that have planets.

sun = get("$..[?(@.planets)].name", solar_system)
assert sun == "Sun"

This search finds all celestial bodies that have a has-moons attribute.

all_celestial_bodies_moon_attribute = [planet for planet in find("$..[?(@['has-moons'])].name", solar_system)]
assert all_celestial_bodies_moon_attribute == ['Mercury', 'Venus', 'Earth', 'Mars', 'Jupiter', 'Saturn', 'Uranus',
                                               'Neptune']

This search finds all celestial bodies that have moons. Note the operator.truth is used to exclude planets that don't have moons.

operator_truth = operator.truth
all_celestial_bodies_moon_attribute = [planet for planet in
                                       find("$..[?(operator_truth(@['has-moons']))].name",
                                            solar_system,
                                            locals=locals()
                                            )]
assert all_celestial_bodies_moon_attribute == ['Earth', 'Mars', 'Jupiter', 'Saturn', 'Uranus', 'Neptune']

has filter comparison operators

Filters can be specified with a comparison operator.

earth = [planet for planet in find("$..[?(@.diameter == 12756)].name", solar_system)]
assert earth == ['Earth']

earth = [planet for planet in find("$..[?(@.diameter != 12756)].name", solar_system)]
assert earth == ['Sun', 'Mercury', 'Venus', 'Mars', 'Jupiter', 'Saturn', 'Uranus', 'Neptune']

earth = [planet for planet in find("$..[?(@.diameter > 12756)].name", solar_system)]
assert earth == ['Sun', 'Jupiter', 'Saturn', 'Uranus', 'Neptune']

earth = [planet for planet in find("$..[?(@.diameter >= 12756)].name", solar_system)]
assert earth == ['Sun', 'Earth', 'Jupiter', 'Saturn', 'Uranus', 'Neptune']

earth = [planet for planet in find("$..[?(@.diameter < 12756)].name", solar_system)]
assert earth == ['Mercury', 'Venus', 'Mars']

earth = [planet for planet in find("$..[?(@.diameter <= 12756)].name", solar_system)]
assert earth == ['Mercury', 'Venus', 'Earth', 'Mars']

There is also a regular expression operator. This example finds all the planets that end with the letter s.

earth = [planet for planet in find(r"$..[?(@.name =~ '\w+s')].name", solar_system)]
assert earth == ['Venus', 'Mars', 'Uranus']

has filter type conversion

Sometimes the value is the wrong type for the comparison operator. In this example the attribute 'Number of Moons' is str type.

planets = [planet for planet in find("$..[?(@['Number of Moons'] > '5')].name", solar_system)]
assert planets == ['Jupiter', 'Saturn']

This is how to convert the type to an int before applying the comparison operator.

planets = [planet for planet in find("$..[?(int(@['Number of Moons']) > 5)].name", solar_system)]
assert planets == ['Jupiter', 'Saturn', 'Uranus', 'Neptune']

has filter comparison operators as single argument functions

A filter operator can be specified as a single argument function. Here an example that searches for planets that have the same diameter as earth.

earths_diameter = partial(operator.eq, 12756)
earth = [planet for planet in find("$..[?(earths_diameter(@.diameter))].name", solar_system, locals=locals())]
assert earth == ['Earth']

Any single argument function can be used as an operator. This example uses a Regular Expression to finds planets that end with the letter s.

name_ends_with_s = re.compile(r"\w+s").match
earth = [planet for planet in find("$..[?(name_ends_with_s(@.name))].name", solar_system, locals=locals())]
assert earth == ['Venus', 'Mars', 'Uranus']

This example uses a closure to find planets that have the same diameter as earth.

def smaller_than_earth(value):
    return value < 12756

earth = [planet for planet in find("$..[?(smaller_than_earth(@.diameter))].name", solar_system, locals=locals())]
assert earth == ['Mercury', 'Venus', 'Mars']

logical and, or and not filters

and

A regular express to test if the second letter in the value is 'a'.

second_letter_is_a = re.compile(r".a.*").fullmatch

The and function evaluates as the logical and operator. It is equivalent to: (arg1 and arg2 and ...)

found = [planet for planet in find("$..[?(@.diameter < 10000 and (second_letter_is_a(@.name)))].name",
                                   solar_system,
                                   locals=locals())
         ]
assert found == ['Mars']

or

The or function evaluates as the logical or operator. It is equivalent to: (arg1 and arg2 and ...)

found = [planet for planet in find("$..[?(@.diameter < 10000 or (second_letter_is_a(@.name)))].name",
                                   solar_system,
                                   locals=locals()
                                   )
         ]
assert found == ['Mercury', 'Earth', 'Mars', 'Saturn']

not

The not function evaluates as the logical not operator. It is equivalent to: (not arg) This example find all the planets names not not equal to Earth. Note the double nots.

found = [planet for planet in find("$..[?(not (@.name != 'Earth'))].name", solar_system)]
assert found == ['Earth']

Combining has, and, or, and not filters.

Each of the has function can be passed as arguments to any of the other has function to construct complex boolean equation. This example is equivalent to: (10000 > diameter or diameter > 20000) and second_letter_is_a(name))

found = [planet for planet in
         find("$..[?((@.diameter < 10000 or @.diameter > 20000) and (second_letter_is_a(@.name)))].name",
              solar_system,
              locals=locals()
              )
         ]
assert found == ['Mars', 'Saturn']

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

jsonpath_tp-1.0.1.tar.gz (19.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

jsonpath_tp-1.0.1-py3-none-any.whl (17.1 kB view details)

Uploaded Python 3

File details

Details for the file jsonpath_tp-1.0.1.tar.gz.

File metadata

  • Download URL: jsonpath_tp-1.0.1.tar.gz
  • Upload date:
  • Size: 19.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.1 CPython/3.7.9

File hashes

Hashes for jsonpath_tp-1.0.1.tar.gz
Algorithm Hash digest
SHA256 c0823fc592b125a4b4434b556db6b28751e594900d3560cb9e3b1236eb5bab51
MD5 b2d62fc75d4464447f9e07284d3fd211
BLAKE2b-256 585ebe1363e32960d85eb42a70712d6ea80738ae0bc079cf8cb26f4e005e70e0

See more details on using hashes here.

File details

Details for the file jsonpath_tp-1.0.1-py3-none-any.whl.

File metadata

  • Download URL: jsonpath_tp-1.0.1-py3-none-any.whl
  • Upload date:
  • Size: 17.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.1 CPython/3.7.9

File hashes

Hashes for jsonpath_tp-1.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 200f09ed0df4bec80e4ee4d71d6779e933bd4d964cc9fbf5deb388d49985c6cb
MD5 64d05beef8ee7f6dbbaba7798229468a
BLAKE2b-256 848e1a367b0c8123c4419a2d04432b95121031fe6f17246baa6f7d3521c4b658

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page