Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .flake8
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[flake8]
# 79 chars is too strict and we don't have 80-char terminals nowadays,
# 160 chars is too much since it doesn't let us use split view efficiently:
max-line-length = 120
4 changes: 2 additions & 2 deletions .github/ISSUE_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
### JSONPath2 version
[Version of the JSONPath2 software where you are encountering the issue]
### jsonpath2 version
[Version of jsonpath2 where you are encountering the issue]

### Platform Details
[Operating system distribution and release version. Cloud provider if running in the cloud]
Expand Down
1 change: 1 addition & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
exclude: 'jsonpath2/parser/JSONPath.*'
repos:
- repo: git://github.com/pre-commit/pre-commit-hooks
rev: v1.2.3
Expand Down
31 changes: 9 additions & 22 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,26 +1,24 @@
language: python
python:
- 2.7
- 3.6
python: 3.6
stages:
- lint
- test
- deploy

install: pip install -r requirements-dev.txt
script:
- coverage run --include='jsonpath2/*' -m pytest -v
- coverage report -m --fail-under 100
- pip install .
- python setup.py bdist_wheel
- python setup.py sdist
jobs:
include:
- stage: lint
python: 3.6
script: pre-commit run -a
- python: 2.7
script: pre-commit run -a
- stage: test
python: 3.6
script:
- coverage run --include='jsonpath2/*' --omit='jsonpath2/parser/JSONPath*' -m pytest -v
- coverage report -m --fail-under 100
- pip install .
- python setup.py bdist_wheel
- python setup.py sdist
- stage: deploy
python: 3.6
script: skip
Expand All @@ -33,14 +31,3 @@ jobs:
distributions: "sdist bdist_wheel"
on:
tags: true
- python: 2.7
script: skip
deploy:
skip_cleanup: true
provider: pypi
user: dmlb2000
password:
secure: MeskzH+TIGe4iboe/VL0+3dSJ5yL/0f8CVH7AcLBmToEArWAOdx5v42fDbOGxSio9ezYdlGyP1fTeBZybIhCvnv44W43iXol2DSkNILdIkfPhsp/RWvZh+qylSldfwiS+gKRtWRCnMpItpmIDMpbBBf/malDLgo41JrhUMeJ2EgvAlRAIDN58VcgZFCyq/cYpo8aRnqvjAmHKwNwEVZP9fFttpys7JXnxxXgP66Yr7WZIVp1v3wv5KwJdqdLlWAL/ZDftTy61ad23sZn0sv3DWYRJ8eJxb2UXQssLyhoZDvAKFoymFhBWoNINpwYDkTZeSQkRPuf1BHgSYRe3nT+71IpXIBF0H7kbmStOttk2Z2kPrlprkZhoZlUwYhRmwgTKWPR2BCyzepDfNKFGoGLz1a98ymb/iqJbBhtuo2ZHH6xsodfKmjVRS8Cx6xCXYyUG5ZW9NK/luMYSNmM78vL6HNcY+yGZ1GS6kXtjUVLPh9CSXld6fuDY/sWWzpXWuhutbfM8+TKNXNF/JOnolJVAgpseDLW3rlNM8jKFLYv1ut/MR/qyoTeMzGe03BgMxX4o5LesVHaWQfvlDubCnXmeRdgYWuxGmFCmRphIu7d3+NwI/ZWWV6dhlqdID1YbdmQJcfz/NPslAn3sXvgLpsmiuSyr2FIuXBbhQozc+xstsQ=
distributions: "bdist_wheel"
on:
tags: true
143 changes: 141 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,142 @@
# Python JSONPath2
# jsonpath2
[![Build Status](https://travis-ci.org/pacifica/python-jsonpath2.svg?branch=master)](https://travis-ci.org/pacifica/python-jsonpath2)
A JSONPath implementation for Python (but better than jsonpath).

This repository contains an implementation of [JSONPath](http://goessner.net/articles/JsonPath/) ([XPath](https://www.w3.org/TR/xpath/all/) for [JSON](https://www.json.org/)) for the Python programming language.

## API

### `Path` class

The `jsonpath2.Path.Path` class represents a JSONPath.

```python
>>> s = '{"hello":"Hello, world!"}'
'{"hello":"Hello, world!"}'
>>> import json
>>> d = json.loads(s)
{'hello':'Hello, world!'}
>>> from jsonpath2.path import Path
>>> p = Path.parse_str('$["hello"]')
<jsonpath2.path.Path object>
>>> list(map(lambda match_data: match_data.current_value, p.match(d)))
['Hello, world!']
>>> list(map(lambda match_data: match_data.node.tojsonpath(), p.match(d)))
['$["hello"]']
```

This class is constructed with respect to the given instance of the `jsonpath2.Path.RootNode` class (viz., the `root_node` property).

#### `parse_str(strdata)` class method

Parse the given string and return a new instance of this class.

#### `parse_file(fileName, encoding='ascii')` class method

Parse the contents of the given file and return a new instance of this class.

#### `match(root_value)` instance method

Match the given JSON data structure against this instance.
For each match, yield an instance of the `jsonpath2.Node.MatchData` class.

#### `__eq__(other)` instance method

Tests if two instances are equal.

#### `__str__()` instance method

Returns the string representation of this instance.

#### `root_node` property

The root node of the abstract syntax tree for this instance.

### `Node` abstract class

The `jsonpath2.Node.Node` class represents the abstract syntax tree for a JSONPath.

#### `__eq__(other)` instance method

Tests if two instances are equal.

#### `__jsonpath__()` instance method

Yields the lexer tokens for the string representation of this instance.

#### `match(root_value, current_value)` instance method

Match the given root and current JSON data structures against this instance.
For each match, yield an instance of the `jsonpath2.Node.MatchData` class.

#### `tojsonpath()` instance method

Returns the string representation of this instance.

### `MatchData` class

The `jsonpath2.Node.MatchData` class represents the JSON value and context for a JSONPath match.

This class is constructed with respect to a root JSON value, a current JSON value, and an abstract syntax tree node.

#### `__eq__(other)` instance method

Tests if two instances are equal.

#### `root_value` property

The root JSON value.

#### `current_value` property

The current JSON value (i.e., the matching JSON value).

#### `node` property

The abstract syntax tree node.

## Syntax

| XPath | JSONPath | Description |
| - | - | - |
| `/` | `$` | the root JSON value |
| `.` | `@` | the current JSON value |
| `/` | `.` or `[]` | child operator |
| `//` | `..` | recursive descent (depth-first search) |
| `*` | `*` | wildcard (all elements of a JSON array; all values of a JSON object; otherwise none) |
| `[]` | `[]` | subscript operator |
| <code>&#124;</code> | `[,]` | union operator (for two or more subscript operators) |
| n/a | `[start:end:step]` | slice operator (subset of elements of a JSON array) |
| `[]` | `?()` | filter expression (for use with subscript operator) |

| JSONPath Filter Expression | Description |
| - | - |
| `$` or `@` | nested JSONPath (returns `true` if any match exists; otherwise, returns `false`) |
| `=`, `!=`, `>`, `>=`, `<`, `<=` | binary operator, where left-hand operand is a nested JSONPath and right-right operand is a JSON value (returns `true` if any match exists; otherwise, returns `false`) |
| `and`, `or`, `not` | Boolean operator, where operands are JSONPath filter expressions |
| `(` ... `)` | parentheses |

## Grammar and parser

The [ANTLR v4](https://github.com/antlr/antlr4) grammar for JSONPath is available at `jsonpath2/parser/JSONPath.g4`.

### Installing ANTLR v4

Adapted from https://github.com/antlr/antlr4/blob/master/doc/getting-started.md.

```bash
cd /usr/local/lib
curl -O http://www.antlr.org/download/antlr-4.7.1-complete.jar

export CLASSPATH=".:/usr/local/lib/antlr-4.7.1-complete.jar:$CLASSPATH"

alias antlr4='java -Xmx500M -cp "/usr/local/lib/antlr-4.7.1-complete.jar:$CLASSPATH" org.antlr.v4.Tool'
alias grun='java org.antlr.v4.gui.TestRig'
```

### Building the parser for the grammar

Adapted from https://github.com/antlr/antlr4/blob/master/doc/python-target.md.

```bash
antlr4 -Dlanguage=Python3 -o . -lib . jsonpath2/parser/JSONPath.g4
```
16 changes: 1 addition & 15 deletions jsonpath2/__init__.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
"""Example Module."""


class Example(object):
"""This is an example class in the example module."""

@staticmethod
def add(thing1, thing2):
"""Add thing one and thing two together."""
return thing1 + thing2

@staticmethod
def mul(thing1, thing2):
"""Multiply thing one and thing two together."""
return thing1 * thing2
"""The jsonpath2 module."""
18 changes: 18 additions & 0 deletions jsonpath2/expression.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
"""Expression module."""
from abc import abstractmethod
from jsonpath2.tojsonpath import ToJSONPath


class Expression(ToJSONPath):
"""Add the expression methods to the jsonpath object."""

def __eq__(self, other: object) -> bool:
"""Test self the same as the other object."""
return isinstance(other, self.__class__) and (self.__dict__ == other.__dict__)

@abstractmethod
def evaluate(self, root_value: object, current_value: object) -> bool: # pragma: no cover abstract method
"""Abstract method to evaluate the expression."""
raise NotImplementedError()
3 changes: 3 additions & 0 deletions jsonpath2/expressions/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
"""Expressions used in jsonpath module."""
Loading