Giter VIP home page Giter VIP logo

jsonpath-ng's Introduction

Python JSONPath Next-Generation Build Status PyPI

A final implementation of JSONPath for Python that aims to be standard compliant, including arithmetic and binary comparison operators, as defined in the original JSONPath proposal.

This packages merges both jsonpath-rw and jsonpath-rw-ext and provides several AST API enhancements, such as the ability to update or remove nodes in the tree.

About

This library provides a robust and significantly extended implementation of JSONPath for Python. It is tested with CPython 3.8 and higher.

This library differs from other JSONPath implementations in that it is a full language implementation, meaning the JSONPath expressions are first class objects, easy to analyze, transform, parse, print, and extend.

Quick Start

To install, use pip:

$ pip install --upgrade jsonpath-ng

Usage

Basic examples:

$ python

>>> from jsonpath_ng import jsonpath, parse

# A robust parser, not just a regex. (Makes powerful extensions possible; see below)
>>> jsonpath_expr = parse('foo[*].baz')

# Extracting values is easy
>>> [match.value for match in jsonpath_expr.find({'foo': [{'baz': 1}, {'baz': 2}]})]
[1, 2]

# Matches remember where they came from
>>> [str(match.full_path) for match in jsonpath_expr.find({'foo': [{'baz': 1}, {'baz': 2}]})]
['foo.[0].baz', 'foo.[1].baz']

# Modifying values matching the path
>>> jsonpath_expr.update( {'foo': [{'baz': 1}, {'baz': 2}]}, 3)
{'foo': [{'baz': 3}, {'baz': 3}]}

# Modifying one of the values matching the path
>>> matches = jsonpath_expr.find({'foo': [{'baz': 1}, {'baz': 2}]})
>>> matches[0].full_path.update( {'foo': [{'baz': 1}, {'baz': 2}]}, 3)
{'foo': [{'baz': 3}, {'baz': 2}]}

# Removing all values matching a path
>>> jsonpath_expr.filter(lambda d: True, {'foo': [{'baz': 1}, {'baz': 2}]})
{'foo': [{}, {}]}

# Removing values containing particular data matching path
>>> jsonpath_expr.filter(lambda d: d == 2, {'foo': [{'baz': 1}, {'baz': 2}]})
{'foo': [{'baz': 1}, {}]}

# And this can be useful for automatically providing ids for bits of data that do not have them (currently a global switch)
>>> jsonpath.auto_id_field = 'id'
>>> [match.value for match in parse('foo[*].id').find({'foo': [{'id': 'bizzle'}, {'baz': 3}]})]
['foo.bizzle', 'foo.[1]']

# A handy extension: named operators like `parent`
>>> [match.value for match in parse('a.*.b.`parent`.c').find({'a': {'x': {'b': 1, 'c': 'number one'}, 'y': {'b': 2, 'c': 'number two'}}})]
['number two', 'number one']

# You can also build expressions directly quite easily
>>> from jsonpath_ng.jsonpath import Fields
>>> from jsonpath_ng.jsonpath import Slice

>>> jsonpath_expr_direct = Fields('foo').child(Slice('*')).child(Fields('baz'))  # This is equivalent

Using the extended parser:

$ python

>>> from jsonpath_ng.ext import parse

# A robust parser, not just a regex. (Makes powerful extensions possible; see below)
>>> jsonpath_expr = parse('foo[*].baz')

JSONPath Syntax

The JSONPath syntax supported by this library includes some additional features and omits some problematic features (those that make it unportable). In particular, some new operators such as | and where are available, and parentheses are used for grouping not for callbacks into Python, since with these changes the language is not trivially associative. Also, fields may be quoted whether or not they are contained in brackets.

Atomic expressions:

Syntax Meaning
$ The root object
`this` The "current" object.
`foo` More generally, this syntax allows "named operators" to extend JSONPath is arbitrary ways
field Specified field(s), described below
[ field ] Same as field
[ idx ] Array access, described below (this is always unambiguous with field access)

Jsonpath operators:

Syntax Meaning
jsonpath1 . jsonpath2 All nodes matched by jsonpath2 starting at any node matching jsonpath1
jsonpath [ whatever ] Same as jsonpath.whatever
jsonpath1 .. jsonpath2 All nodes matched by jsonpath2 that descend from any node matching jsonpath1
jsonpath1 where jsonpath2 Any nodes matching jsonpath1 with a child matching jsonpath2
jsonpath1 | jsonpath2 Any nodes matching the union of jsonpath1 and jsonpath2

Field specifiers ( field ):

Syntax Meaning
fieldname the field fieldname (from the "current" object)
"fieldname" same as above, for allowing special characters in the fieldname
'fieldname' ditto
* any field
field , field either of the named fields (you can always build equivalent jsonpath using |)

Array specifiers ( idx ):

Syntax Meaning
[n] array index (may be comma-separated list)
[start?:end?] array slicing (note that step is unimplemented only due to lack of need thus far)
[*] any array index

Programmatic JSONPath

If you are programming in Python and would like a more robust way to create JSONPath expressions that does not depend on a parser, it is very easy to do so directly, and here are some examples:

  • Root()
  • Slice(start=0, end=None, step=None)
  • Fields('foo', 'bar')
  • Index(42)
  • Child(Fields('foo'), Index(42))
  • Where(Slice(), Fields('subfield'))
  • Descendants(jsonpath, jsonpath)

Extras

  • Path data: The result of JsonPath.find provide detailed context and path data so it is easy to traverse to parent objects, print full paths to pieces of data, and generate automatic ids.
  • Automatic Ids: If you set jsonpath_ng.auto_id_field to a value other than None, then for any piece of data missing that field, it will be replaced by the JSONPath to it, giving automatic unique ids to any piece of data. These ids will take into account any ids already present as well.
  • Named operators: Instead of using @ to reference the current object, this library uses `this`. In general, any string contained in backquotes can be made to be a new operator, currently by extending the library.

Extensions

To use the extensions below you must import from jsonpath_ng.ext.

name Example
len
  • $.objects.`len`
sub
  • $.field.`sub(/foo\\\\+(.*)/, \\\\1)`
  • $.field.`sub(/regex/, replacement)`
split
  • $.field.`split(+, 2, -1)`
  • $.field.`split(sep, segement, maxsplit)`
sorted
  • $.objects.`sorted`
  • $.objects[\\some_field]
  • $.objects[\\some_field,/other_field]
filter
  • $.objects[?(@some_field > 5)]
  • $.objects[?some_field = "foobar"]
  • $.objects[?some_field =~ "foobar"]
  • $.objects[?some_field > 5 & other < 2]

Supported operators: - Equality: ==, =, != - Comparison: >, >=, <, <= - Regex match: =~

Combine multiple criteria with '&'.

Properties can only be compared to static values.

arithmetic (-+*/)
  • $.foo + "_" + $.bar
  • $.foo * 12
  • $.objects[*].cow + $.objects[*].cat

About arithmetic and string

Operations are done with python operators and allows types that python allows, and return [] if the operation can be done due to incompatible types.

When operators are used, a jsonpath must be be fully defined otherwise jsonpath-rw-ext can't known if the expression is a string or a jsonpath field, in this case it will choice string as type.

Example with data:

{
    'cow': 'foo',
    'fish': 'bar'
}
cow + fish returns cowfish
$.cow + $.fish returns foobar
$.cow + "_" + $.fish returns foo_bar
$.cow + "_" + fish returns foo_fish

About arithmetic and list

Arithmetic can be used against two lists if they have the same size.

Example with data:

{'objects': [
    {'cow': 2, 'cat': 3},
    {'cow': 4, 'cat': 6}
]}
$.objects[\*].cow + $.objects[\*].cat returns [6, 9]

More to explore

There are way too many JSONPath implementations out there to discuss. Some are robust, some are toy projects that still work fine, some are exercises. There will undoubtedly be many more. This one is made for use in released, maintained code, and in particular for programmatic access to the abstract syntax and extension. But JSONPath at its simplest just isn't that complicated, so you can probably use any of them successfully. Why not this one?

The original proposal, as far as I know:

Other examples

Loading json data from file

import json
d = json.loads('{"foo": [{"baz": 1}, {"baz": 2}]}')
# or
with open('myfile.json') as f:
    d = json.load(f)

Special note about PLY and docstrings

The main parsing toolkit underlying this library, PLY, does not work with docstrings removed. For example, PYTHONOPTIMIZE=2 and python -OO will both cause a failure.

Contributors

This package is authored and maintained by:

with the help of patches submitted by these contributors.

Copyright and License

Copyright 2013 - Kenneth Knowles

Copyright 2017 - Tomas Aparicio

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

jsonpath-ng's People

Contributors

back2root avatar dchourasia avatar dcreemer avatar dependabot[bot] avatar domdfcoding avatar evgenus avatar fabaff avatar frewsxcv avatar gera avatar gregglind avatar h2non avatar hakanw avatar iley avatar jfardello avatar joshbenner avatar kaapstorm avatar kennknowles avatar kharakawa avatar kmmbvnr avatar kurtmckee avatar memborsky avatar michaelmior avatar nchammas avatar nivesnine avatar oskarrrrrrr avatar rahendatri avatar remirigal avatar rolling-robot avatar snopoke avatar tomas-fp avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

jsonpath-ng's Issues

way to combine array operations?

Is there a way to both filter an array and get an index or slice of the filtered values?

Something like $.foo[?(@value>=3)],[1:5].baz, which would filter for filter for objects with value lte 3 and then return items 1-5 of the filtered objects. Or maybe $.foo[?(@value>=3),1:5].baz? Not sure what appropriate syntax would be for this.

I've looked at a bunch of jsonpath and similar (objectpath, sakstig) implementations and none seem to have a technique for this use case.

I'm having an error trying to create a JSONPath - Exception: Parse error at 1: 4 near token? (?)

Hi!

I am trying to create a filter on the following JSON but for some reason I am getting the error all the time:

Exception: Parse error at 1: 4 near token? (?)

This is my code:

from jsonpath_ng import jsonpath
from jsonpath_ng.ext import parse

r = requests.get(url, headers=json.loads(headers))
j = r.text
j_exp = parse("$..[?(@name == 'CCAA')].idAssetType")
id = j_exp.find(j)
print(id)

Confirm that the value of j is:

[{"idAssetType":6,"name":"CCAA","enterprise":{"id":1,"name":"APV"}}]

README.rst examples are syntactically wrong

In README.rst - the examples at line 205 to 207are syntatically wrong (unbalanced '()' brackets).

203: +--------------+----------------------------------------------+
204: | filter       | - $.objects[?(@some_field > 5)]              |
205: |              | - $.objects[?some_field = "foobar")]         |
206: |              | - $.objects[?some_field =~ "foobar")]        |
207: |              | - $.objects[?some_field > 5 & other < 2)]    |
208: +--------------+----------------------------------------------+

I do not know what it should be yet - but the parser doesn't like it.

filter problem

I try to use filter to find data with "akey"="idcard". Here is my code.

from jsonpath_ng import jsonpath
from jsonpath_ng.ext import parse

data = [
{"akey":"idcard","group":12},
{"akey":"uid","group":14},
{"akey":"name","group":8},
]

[m.value for m in parse("$.[?(@akey='idcard'])").find(data)]

Runtim error is:

Traceback (most recent call last):
File "", line 1, in
File "/Users/sunziyi/Documents/py_env/bmtag/lib/python2.7/site-packages/jsonpath_ng/ext/parser.py", line 170, in parse
return ExtentedJsonPathParser(debug=debug).parse(path)
File "/Users/sunziyi/Documents/py_env/bmtag/lib/python2.7/site-packages/jsonpath_ng/parser.py", line 32, in parse
return self.parse_token_stream(lexer.tokenize(string))
File "/Users/sunziyi/Documents/py_env/bmtag/lib/python2.7/site-packages/jsonpath_ng/parser.py", line 55, in parse_token_stream
return new_parser.parse(lexer = IteratorToTokenStream(token_iterator))
File "/Users/sunziyi/Documents/py_env/bmtag/lib/python2.7/site-packages/ply/yacc.py", line 331, in parse
return self.parseopt_notrack(input, lexer, debug, tracking, tokenfunc)
File "/Users/sunziyi/Documents/py_env/bmtag/lib/python2.7/site-packages/ply/yacc.py", line 1199, in parseopt_notrack
tok = call_errorfunc(self.errorfunc, errtoken, self)
File "/Users/sunziyi/Documents/py_env/bmtag/lib/python2.7/site-packages/ply/yacc.py", line 193, in call_errorfunc
r = errorfunc(token)
File "/Users/sunziyi/Documents/py_env/bmtag/lib/python2.7/site-packages/jsonpath_ng/parser.py", line 69, in p_error
raise Exception('Parse error at %s:%s near token %s (%s)' % (t.lineno, t.col, t.value, t.type))
Exception: Parse error at 1:3 near token ? (?)

Question mark cannot be used?

Importing test library 'REST' failed: ModuleNotFoundError: No module named 'jsonpath_ng.bin'; 'jsonpath_ng' is not a package

For jsonpath-ng 1.5.0 released today got some issue

Importing test library 'REST' failed: ModuleNotFoundError: No module named 'jsonpath_ng.bin'; 'jsonpath_ng' is not a package

Traceback (most recent call last):
File "/usr/local/lib/python3.8/site-packages/REST/init.py", line 37, in
from .keywords import Keywords
File "/usr/local/lib/python3.8/site-packages/REST/keywords.py", line 34, in
from jsonpath_ng.ext import parse as parse_jsonpath
File "/usr/local/bin/jsonpath_ng.py", line 5, in
from jsonpath_ng.bin.jsonpath import entry_point

Please Note: Working fine with jsonpath-ng==1.4.3

Documentation: possible typo in "About arithmetic and string"

In this paragraph

About arithmetic and string
Operations are done with python operators and allows types that python allows, and return [] if the operation can be done due to incompatible types.

I think the last sentence should be: ".. if the operation cannot be done due to incompatible.."

Unable to pass regex in jsonpath

Hi,

I'm unable to pass the regex in the parse as follows
jsonpath_expr = parse("$..entries[[email protected] =~ /Mani/i]")

getting the following error
raise Exception('Parse error at %s:%s near token %s (%s)' % (t.lineno, t.col, t.value, t.type))
Exception: Parse error at 1:22 near token / (SORT_DIRECTION)

my json looks like this
image

1.5.0 python3 issue

https://pypi.org/project/jsonpath-ng/#files not found py3 wheel file?

Today I run pip3 install -U jsonpath_ng

jsonpath_ng-1.5.0-py3-none-any.whl downloaded, but after unziping the wheel file, __init__.py is 1.4.3, and the master branch is also 1.4.3

__version__ = '1.4.3'

__version__ = '1.4.3'

Test code:

from jsonpath_ng.ext import parse as jp_parse

On windows python3.7 it works well

but on linux (ubuntu18) python3.7, 3.8 it raised an error

E ModuleNotFoundError: No module named 'jsonpath_ng.ext'; 'jsonpath_ng' is not a package

linux pip log snippets

Collecting jsonpath_ng
  Downloading jsonpath-ng-1.5.0.tar.gz (30 kB)
Running setup.py install for jsonpath-ng: started
    Running setup.py install for jsonpath-ng: finished with status 'done'

image

View the pip log, it says build error, but Successfully installed decorator-4.4.2 jsonpath-ng-1.5.0 ply-3.11 six-1.14.0

Exception: Parse error at 1:4 near token / (SORT_DIRECTION)

Hello,

I am getting the exception while execution following example:

import json
import jsonpath_rw_ext
text = '{"name/subname": "value"}'
json = json.loads(text)
matches = jsonpath_rw_ext.match("name/subname", json)

How to escape the character '/', please?

Thank you.
Petr

Creating a dict

Hi,

nice library!

I want to build a dictionary based on jsonpath, string (values) pairs. Is that somehow possible?
thanks

Simple filter doesn't work

In [133]: jsonpath_ng.__version__                                                                                                                 
Out[133]: '1.4.3'

In [134]: from jsonpath_ng.ext import parse                                                                                                       

In [135]: parse('a[?(@.b > 1)]').find({'a': {'b': 2}})                                                                                            
Out[135]: []

Im trying to filter something more complex, but I can't get any filtering to work at all, even with this simple example, I verified it works on http://jsonpath.com/

Return null for missing property/leaf

given

[
    {"name": "Test Account"},
    {}
]

$.[*].name returns

[
   "Test Account"
]

but I'd like for it to return

[
   "Test Account",
    None
]

https://jsonpath.herokuapp.com/ let's you decide whether or not to return null in this case. Does such a flag exist here?

EDIT:

I found the find_or_create method, which would put an empty dict in the place of None. Would it be possible to customize the created value?

Unknown named operator `len`

Installation log:

C:\WINDOWS\system32>pip install jsonpath-ng
Collecting jsonpath-ng
  Downloading jsonpath_ng-1.4.3-py2.py3-none-any.whl
Requirement already satisfied: decorator in c:\python27\lib\site-packages (from jsonpath-ng)
Requirement already satisfied: ply in c:\python27\lib\site-packages (from jsonpath-ng)
Requirement already satisfied: six in c:\python27\lib\site-packages (from jsonpath-ng)
Installing collected packages: jsonpath-ng
Successfully installed jsonpath-ng-1.4.3

C:\WINDOWS\system32>pip install -U jsonpath-ng
Requirement already up-to-date: jsonpath-ng in c:\python27\lib\site-packages
Requirement already up-to-date: decorator in c:\python27\lib\site-packages (from jsonpath-ng)
Collecting ply (from jsonpath-ng)
  Downloading ply-3.10.tar.gz (150kB)
    100% |################################| 153kB 1.4MB/s
Collecting six (from jsonpath-ng)
  Downloading six-1.11.0-py2.py3-none-any.whl
Building wheels for collected packages: ply
  Running setup.py bdist_wheel for ply ... done
  Stored in directory: C:\Users\alexander.iljushkin\AppData\Local\pip\Cache\wheels\ad\dd\ad\8ce1991a7b380dfe23d6cc81a4de5c2775bc728b5a0a7721aa
Successfully built ply
Installing collected packages: ply, six
  Found existing installation: ply 3.8
    Uninstalling ply-3.8:
      Successfully uninstalled ply-3.8
  Found existing installation: six 1.10.0
    Uninstalling six-1.10.0:
      Successfully uninstalled six-1.10.0
Successfully installed ply-3.10 six-1.11.0

Script:

# coding: utf-8

import json

import jsonpath_ng as jng
import jsonpath_rw as jrw
import jsonpath_rw_ext as jex

s = u"""
{
"menu": {
    "id": "file",
    "value": "File",
    "popup": {
      "menuitem": [
        {
          "value": "New",
          "onclick": "CreateNewDoc()"
        },
        {
          "value": "Open",
          "onclick": "OpenDoc()"
        },
        {
          "value": "Close",
          "onclick": "CloseDoc()"
        }
      ]
    }
  }
}
"""

if __name__ == '__main__':
    jsonpath_expr = jng.parse("$..menuitem.`len`")
    search = jsonpath_expr.find(json.loads(s))

Error:

pydev debugger: process 21076 is connecting

Connected to pydev debugger (build 172.3968.37)
Traceback (most recent call last):
  File "C:\Program Files\JetBrains\PyCharm 2017.2.3\helpers\pydev\pydevd.py", line 1599, in <module>
    globals = debugger.run(setup['file'], None, None, is_module)
  File "C:\Program Files\JetBrains\PyCharm 2017.2.3\helpers\pydev\pydevd.py", line 1026, in run
    pydev_imports.execfile(file, globals, locals)  # execute the script
  File "C:/Users/alexander.iljushkin/Desktop/test.py", line 35, in <module>
    jsonpath_expr = jng.parse("$..menuitem.`len`")
  File "C:\Python27\lib\site-packages\jsonpath_ng\parser.py", line 14, in parse
    return JsonPathParser().parse(string)
  File "C:\Python27\lib\site-packages\jsonpath_ng\parser.py", line 32, in parse
    return self.parse_token_stream(lexer.tokenize(string))
  File "C:\Python27\lib\site-packages\jsonpath_ng\parser.py", line 55, in parse_token_stream
    return new_parser.parse(lexer = IteratorToTokenStream(token_iterator))
  File "C:\Python27\lib\site-packages\ply\yacc.py", line 331, in parse
    return self.parseopt_notrack(input, lexer, debug, tracking, tokenfunc)
  File "C:\Python27\lib\site-packages\ply\yacc.py", line 1118, in parseopt_notrack
    p.callable(pslice)
  File "C:\Python27\lib\site-packages\jsonpath_ng\parser.py", line 101, in p_jsonpath_named_operator
    raise Exception('Unknown named operator `%s` at %s:%s' % (p[1], p.lineno(1), p.lexpos(1)))
Exception: Unknown named operator `len` at 1:16

Multiple "." periods in dictionary key name

We are attempting to use this package to parse values from a json payload that has keys with periods (.) in the name. It does not seem to support these and I provided two example use cases below. These are both technically valid dict and json objects.

Input (Non-Nested):

{
    "data": {
        "entity": {
            "com.hello.DS": 123456 
        }
    }
}

Attempted JSONPath: data.entity['com.hello.DS'] and data.entity.['com.hello.DS']

Input (Nested):

{
    "data": {
        "entity": {
            "com.hello.DS": {
                "value": 123456
            }
        }
    }
}

Attempted JSONPath: data.entity['com.hello.DS'].value and data.entity.['com.hello.DS'].value

Filter a top level field

Given:

{
    "store": {
        "bicycle": {
            "color": "red",
            "price": 19.95
        }
    },
    "expensive": 10
}

The expressions $[?($.expensive)] (look for the root/top level expensive field) or $[?($.expensive == "10")] (check its value) both return:

[
   {
      "store" : {
         "bicycle" : {
            "color" : "red",
            "price" : 19.95
         }
      },
      "expensive" : 10
   }
]

with the Jayway JSONPath (test them on jsonpah.herokuapp.com).

The same expressions return an empty list with jsonpath-ng. Not sure whether this is a bug in jsonpath-ng or its expected behaviour (difficult to say since there's no formal JSONPATH spec).

The workaround is simple though:

import json
def filter_json_toplevel(file: filepath, field: str, val: str) -> list
    data = json.load(file)
    if data.get(field) == val:
        return [data]
    else:
        return []

unexpected result with child-selector and multiple fields

The path id,name,child.[*].id will only return the value of child.[*].id and id,name is ignored.

Example code to reproduce:

from jsonpath_ng import parse


data = {'id': 'foo', 'name': 'Foo', 'child': [{'id': 'child-foo'}]}

cases = (
    ('id,name', ['foo', 'Foo']),
    ('id,name,child.[*]', ['foo', 'Foo', {'id': 'child-foo'}]),
    ('id,name,child.[*].id', ['foo', 'Foo', 'child-foo']),  # Error
)

for path, expected in cases:
    actual = [m.value for m in parse(path).find(data)]
    if expected != actual:
        print('parser: ' + path)
        print('actual: ' + str(actual))
        print('expected: ' + str(expected))

# prints out:
# parser: id,name,child.[*].id
# actual: ['child-foo']
# expected: ['foo', 'Foo', 'child-foo']

Missing v1.4.3 tag

The newest version seems to be 1.4.3 on pypi but there is no tag for it. This version seems to be uploaded some commit after this one: 8e4693b

Please tag this version in git so it is clear what version of sources 1.4.3 really specially for those that are not using pip to install it (I am packaging it).

filter() throws NotImplementedError when there is a filter expression in jsonpath

The filter() function does not work when there is a filter expression in the jsonpath, and it throws a NotImplementedError.
Code to duplicate this error:

obj = {
    "a": [
        {"b": "X"},
        {"b": "Y"}
    ]
}

jsonpath = parse("$.a[?(@.b==X)]")

matches = jsonpath.find(obj)
print(matches[0].value)
>>> {'b': 'X'}  # find() returns correct value

result = jsonpath.filter(lambda x: True, obj)
>>> NotImplementedError  # filter() throws error

Exception: Parse error at 1:3 near token ] (])

Hello,

When trying to enter the following as my jsonpath, I get the a parse error:

jsonpath_expr = parse('$.[]groupName,tabLabel')

This is a valid json expression, so it should work. I am trying to grab both of the two keys listed in the expression at once.

Is there another way to grab these two keys that will work with jsonpath-ng?

Error:
Traceback (most recent call last):
File "", line 1, in
File "/Users/melissa.montanez/Library/Python/3.8/lib/python/site-packages/jsonpath_ng/parser.py", line 14, in parse
return JsonPathParser().parse(string)
File "/Users/melissa.montanez/Library/Python/3.8/lib/python/site-packages/jsonpath_ng/parser.py", line 32, in parse
return self.parse_token_stream(lexer.tokenize(string))
File "/Users/melissa.montanez/Library/Python/3.8/lib/python/site-packages/jsonpath_ng/parser.py", line 55, in parse_token_stream
return new_parser.parse(lexer = IteratorToTokenStream(token_iterator))
File "/Users/melissa.montanez/Library/Python/3.8/lib/python/site-packages/ply/yacc.py", line 333, in parse
return self.parseopt_notrack(input, lexer, debug, tracking, tokenfunc)
File "/Users/melissa.montanez/Library/Python/3.8/lib/python/site-packages/ply/yacc.py", line 1201, in parseopt_notrack
tok = call_errorfunc(self.errorfunc, errtoken, self)
File "/Users/melissa.montanez/Library/Python/3.8/lib/python/site-packages/ply/yacc.py", line 192, in call_errorfunc
r = errorfunc(token)
File "/Users/melissa.montanez/Library/Python/3.8/lib/python/site-packages/jsonpath_ng/parser.py", line 69, in p_error
raise Exception('Parse error at %s:%s near token %s (%s)' % (t.lineno, t.col, t.value, t.type))
Exception: Parse error at 1:3 near token ] (])

Using the or operator

I have a json structure (client_json) that looks like:

client_json = {
    "data": [
        {
            "attributes": {
                "creators": [
                    {
                        "name": "This is a person",
                        "nameType": "Personal",
                        "givenName": "the",
                        "familyName": "person"
                    },
                    {
                        "name": "This is an organization",
                        "nameType": "Organizational",
                        "givenName": "the",
                        "familyName": "organization"
                    }
                ]
            }
        }
    ]
}

I am trying to use jsonpath-ng 1.4.3 (https://github.com/h2non/jsonpath-ng) to retrieve creator names.

from jsonpath_ng import jsonpath
from jsonpath_ng.ext import parse

These two expressions give the expected results:

[match.value['name'] for match in parse("data[*].attributes.creators[?(@.nameType='Personal')]").find(client_json)]
[match.value['name'] for match in parse("data[*].attributes.creators[?(@.nameType='Organizational')]").find(client_json)]

I am trying to combine them with an or operator and all of these expressions yield parse errors:

parse("data[*].attributes.creators[?(@.nameType = 'Organizational' | @.nameType = 'Personal')]")
parse("data[*].attributes.creators[?(@.nameType == 'Organizational' || @.nameType == 'Personal')]")
parse("data[*].attributes.creators[?(@.nameType = 'Organizational') | ?(@.nameType = 'Personal')]")
parse("data[*].attributes.creators[?(@.nameType == 'Organizational') || ?(@.nameType == 'Personal')]")
parse("data[*].attributes.creators[?(@.nameType == 'Organizational') | ?(@.nameType == 'Personal')]")

Thanks for the help.

Updating a JSON document fails if one of many keys within an path has null value

Steps to reproduce:

import json
from jsonpath_ng import parse

json_document = json.loads('{"elements":[{"sub": {"key":"1"}}, {"sub": null}, {"sub": {"key":"3"}}]}')
jsonpath_expr = parse("$.elements[*].sub.key")

print(jsonpath_expr.update(json_document, "--"))

Expected

# python test.py
{u'elements': [{u'sub': {u'key': '--'}}, {u'sub': None}, {u'sub': {u'key': '--'}}]}

Observed

# python test.py
Traceback (most recent call last):
  File "test.py", line 7, in <module>
    print(jsonpath_expr.update(json_document, "--"))
  File "/opt/splunk/etc/apps/TA-json_redact/lib/jsonpath_ng/jsonpath.py", line 261, in update
    self.right.update(datum.value, val)
  File "/opt/splunk/etc/apps/TA-json_redact/lib/jsonpath_ng/jsonpath.py", line 526, in update
    if field in data:
TypeError: argument of type 'NoneType' is not iterable

specifying a comma-separated list of array indexes throws an Exception

According to the README, a comma-separated list of array indexes may be specified in a JsonPath.

Syntax Meaning
[n] array index (may be comma-separated list)

However, when attempting to use multiple indexes, the following exception is thrown:

def p_error(self, t):
>       raise Exception('Parse error at %s:%s near token %s (%s)' % (t.lineno, t.col, t.value, t.type))
E       Exception: Parse error at 1:14 near token , (,)

The following code can be used to reproduce the issue:

import unittest

from jsonpath_ng.ext import parse

TEST_DATA = {
    'store': {
        'book': [
            {
                'category': 'reference',
                'author': 'Nigel Rees',
                'title': 'Sayings of the Century',
                'price': 8.95
            },
            {
                'category': 'fiction',
                'author': 'Evelyn Waugh',
                'title': 'Sword of Honour',
                'price': 12.99
            },
            {
                'category': 'fiction',
                'author': 'Herman Melville',
                'title': 'Moby Dick',
                'isbn': '0-553-21311-3',
                'price': 8.99
            }
        ],
        'bicycle': {
            'color': 'red',
            'price': 19.95
        }
    }
}


class Test(unittest.TestCase):

    def test(self):
        path = "$.store.book[0,2].title"
        values = [match.value for match in parse(path).find(TEST_DATA)]
        self.assertEqual(values, ['Sayings of the Century', 'Moby Dick'])

I have verified that the path is valid on various JsonPath evaluators.

Equality filter doesn't work with nested array

Been at this all day from different angles trying to figure out why the filter isn't working.

>>> data
{'accounts': [{'regions': ['us-east-1', 'sa-east-1', 'ap-east-1'], 'vpcs': [{'subnets': ['10.0.0.0/24', '10.0.1.0/24'], 'region': 'us-east-1', 'name': 'main'}, {'subnets': ['172.1.1.0/24', '172.1.2.0/24'], 'name': 'vpc1'}], 'name': 'galluptest'}, {'regions': ['us-west-2', 'sa-east-1'], 'vpcs': [{'subnets': ['10.1.1.0/24', '10.1.2.0/24'], 'region': 'us-west-2', 'name': 'default'}, {'subnets': ['172.16.1.0/24', '172.16.2.0/24'], 'name': 'alt'}], 'name': 'ztest'}]}

>>> 
>>> p = parse('$.accounts[*][?name = "ztest"].name')
>>> p.find(data)
[]
>>> p
Child(Child(Child(Child(Root(), Fields('accounts')), Slice(start=None,end=None,step=None)), Filter([Expression(Fields('name') = u'ztest')])), Fields('name'))

>>> [match.value for match in p.find(data)]
[]
>>> 

What am doing wrong?

Thanks.

problem querying: object of type 'NoneType' has no len()

Hello!

First of all: great job on the jsonpath-ng library. I have been using it for 2 weeks now and it fits my purpose as a glove.

Unfortunately, I've run into a document today that is valid json and that I am able to query using jsonpath.com but it causes an error in jsonpath-ng. I am trying to debug it but as of now I have not been able to pinpoint what is causing this.

The document is rather big, so I have attached a PoC python script which loads the JSON document and tries to query it. This way you can see whether I am using the library in the intended way.

I would appreciate any help!

Thanks,

Jerome
jsonpath_bug.zip

filter expression not working?

Hi

I have a simple filter which works on http://jsonpath.com. I'm using jsonpath_ng 1.4.3

Test script:
from jsonpath_ng import jsonpath from jsonpath_ng.ext import parse f = parse('$..[?(@._env=="foo")]').find(data). # should return only the first object

Forgot - even though I'm using the ext parser, I'm getting
File "/Library/Python/2.7/site-packages/jsonpath_ng/parser.py", line 69, in p_error raise Exception('Parse error at %s:%s near token %s (%s)' % (t.lineno, t.col, t.value, t.type)) Exception: Parse error at 1:7 near token ? (?)

Any help is appreciated!!

data = { "wwrc-stg-lom01.someco.com": { "_env": "foo", "_owner": "samwise_gamgee", "_parent": "wwrc-stg-hv1.rtl.someco.com", "dns1": "17.34.100.1", "dns2": "17.34.100.2", "domain": "rtl.someco.com", "env": "p", "gateway": "10.16.64.1", "hostname": "wwrc-stg-lom01", "ipaddr": "10.16.64.11", "netmask": "255.255.255.0", "rollout": "r145", "type": "ilo" }, "wwrc-stg-lom05.rtl.someco.com": { "_env": "prod", "_owner": "Frodo Baggins", "_parent": "wwrc-stg-hv5.rtl.someco.com", "dns1": "17.34.100.1", "dns2": "17.34.100.2", "domain": "rtl.someco.com", "env": "p", "gateway": "10.16.64.1", "hostname": "wwrc-stg-lom05", "ipaddr": "10.16.64.15", "netmask": "255.255.255.0", "rollout": "r145", "type": "ilo" } }

Regular expression not supported in jsonpath array value matching

I believe that the =~ syntax is something Jayway uses, but was unable to find any json filter regex matching when using jsonpath-ng parse function.

when trying to use variable[?(@.parameter =~ 'regex')]
I obtain the following error:

jsonpath_ng.lexer.JsonPathLexerError: Error on line 1, col 63: Unexpected character: ~

Add OR and AND operators

There are already arithmetic operators (+-*/) in the extended version, so I think the next logical step would be to add the OR (|) and AND (&) operators.

For example:

{"a": "a"}
{"b": "b"}
"$.c | $.b" will return "b"
"$.a & $.b" will return "ab"

I hope that these examples have been understood to work. I think it is a basic functionality for the extended version that could enhance its use much more.

Cannot filter leaf objects content

I try to match parse trees obtained from clang -cc1 -dump-ast=json, it works well expect for the leaf objects which I cannot filter against their content.

For instance, on the data provided below, I can do:

import json, pprint
from jsonpath_ng.ext import parse as jp_parse

data = json.load(open("foo.json"))

expr = jp_parse("$..*[?kind='FunctionDecl' & name='fact']"
                "..*[?kind='CallExpr']"
                "..referencedDecl")
for i, match in enumerate(expr.find(data)) :
    print(f"### {i} ###")
    pprint.pprint(match.value)

This prints two matches:

### 0 ###
{'id': '0x6fdad08',
 'kind': 'FunctionDecl',
 'name': 'fact',
 'type': {'qualType': 'int (int)'}}
### 1 ###
{'id': '0x6fdac30',
 'kind': 'ParmVarDecl',
 'name': 'n',
 'type': {'qualType': 'int'}}

But then, if I change the end of the expression to apply filters on the matches above, it does not match anything. For instance, replacing the third line of the expression with "..referencedDecl[?kind='FunctionDecl' & name='fact']", or "..referencedDecl[?name='fact']", or even "..referencedDecl[?name]"does not return any match.

Either there is something I missed in expressions meaning, or this may be a bug.

Thanks in advance for your help.
Franck

The data in foo.json:

{
  "id": "0x6f9b4f8",
  "kind": "TranslationUnitDecl",
  "loc": {},
  "range": {
    "begin": {},
    "end": {}
  },
  "inner": [
    {
      "id": "0x6f9bdb8",
      "kind": "TypedefDecl",
      "loc": {},
      "range": {
        "begin": {},
        "end": {}
      },
      "isImplicit": true,
      "name": "__int128_t",
      "type": {
        "qualType": "__int128"
      },
      "inner": [
        {
          "id": "0x6f9ba90",
          "kind": "BuiltinType",
          "type": {
            "qualType": "__int128"
          }
        }
      ]
    },
    {
      "id": "0x6f9be28",
      "kind": "TypedefDecl",
      "loc": {},
      "range": {
        "begin": {},
        "end": {}
      },
      "isImplicit": true,
      "name": "__uint128_t",
      "type": {
        "qualType": "unsigned __int128"
      },
      "inner": [
        {
          "id": "0x6f9bab0",
          "kind": "BuiltinType",
          "type": {
            "qualType": "unsigned __int128"
          }
        }
      ]
    },
    {
      "id": "0x6f9c130",
      "kind": "TypedefDecl",
      "loc": {},
      "range": {
        "begin": {},
        "end": {}
      },
      "isImplicit": true,
      "name": "__NSConstantString",
      "type": {
        "qualType": "struct __NSConstantString_tag"
      },
      "inner": [
        {
          "id": "0x6f9bf00",
          "kind": "RecordType",
          "type": {
            "qualType": "struct __NSConstantString_tag"
          },
          "decl": {
            "id": "0x6f9be80",
            "kind": "RecordDecl",
            "name": "__NSConstantString_tag"
          }
        }
      ]
    },
    {
      "id": "0x6f9c1d8",
      "kind": "TypedefDecl",
      "loc": {},
      "range": {
        "begin": {},
        "end": {}
      },
      "isImplicit": true,
      "name": "__builtin_ms_va_list",
      "type": {
        "qualType": "char *"
      },
      "inner": [
        {
          "id": "0x6f9c190",
          "kind": "PointerType",
          "type": {
            "qualType": "char *"
          },
          "inner": [
            {
              "id": "0x6f9b590",
              "kind": "BuiltinType",
              "type": {
                "qualType": "char"
              }
            }
          ]
        }
      ]
    },
    {
      "id": "0x6fdabc0",
      "kind": "TypedefDecl",
      "loc": {},
      "range": {
        "begin": {},
        "end": {}
      },
      "isImplicit": true,
      "name": "__builtin_va_list",
      "type": {
        "qualType": "struct __va_list_tag [1]"
      },
      "inner": [
        {
          "id": "0x6f9c470",
          "kind": "ConstantArrayType",
          "type": {
            "qualType": "struct __va_list_tag [1]"
          },
          "size": 1,
          "inner": [
            {
              "id": "0x6f9c2b0",
              "kind": "RecordType",
              "type": {
                "qualType": "struct __va_list_tag"
              },
              "decl": {
                "id": "0x6f9c230",
                "kind": "RecordDecl",
                "name": "__va_list_tag"
              }
            }
          ]
        }
      ]
    },
    {
      "id": "0x6fdad08",
      "kind": "FunctionDecl",
      "loc": {
        "offset": 4,
        "file": "foo.c",
        "line": 1,
        "col": 5,
        "tokLen": 4
      },
      "range": {
        "begin": {
          "offset": 0,
          "col": 1,
          "tokLen": 3
        },
        "end": {
          "offset": 88,
          "line": 7,
          "col": 1,
          "tokLen": 1
        }
      },
      "isReferenced": true,
      "name": "fact",
      "mangledName": "fact",
      "type": {
        "qualType": "int (int)"
      },
      "inner": [
        {
          "id": "0x6fdac30",
          "kind": "ParmVarDecl",
          "loc": {
            "offset": 14,
            "line": 1,
            "col": 15,
            "tokLen": 1
          },
          "range": {
            "begin": {
              "offset": 10,
              "col": 11,
              "tokLen": 3
            },
            "end": {
              "offset": 14,
              "col": 15,
              "tokLen": 1
            }
          },
          "isUsed": true,
          "name": "n",
          "mangledName": "n",
          "type": {
            "qualType": "int"
          }
        },
        {
          "id": "0x6fdb068",
          "kind": "CompoundStmt",
          "range": {
            "begin": {
              "offset": 17,
              "col": 18,
              "tokLen": 1
            },
            "end": {
              "offset": 88,
              "line": 7,
              "col": 1,
              "tokLen": 1
            }
          },
          "inner": [
            {
              "id": "0x6fdb040",
              "kind": "IfStmt",
              "range": {
                "begin": {
                  "offset": 21,
                  "line": 2,
                  "col": 3,
                  "tokLen": 2
                },
                "end": {
                  "offset": 86,
                  "line": 6,
                  "col": 3,
                  "tokLen": 1
                }
              },
              "hasElse": true,
              "inner": [
                {
                  "id": "0x6fdae50",
                  "kind": "BinaryOperator",
                  "range": {
                    "begin": {
                      "offset": 25,
                      "line": 2,
                      "col": 7,
                      "tokLen": 1
                    },
                    "end": {
                      "offset": 30,
                      "col": 12,
                      "tokLen": 1
                    }
                  },
                  "type": {
                    "qualType": "int"
                  },
                  "valueCategory": "rvalue",
                  "opcode": "<=",
                  "inner": [
                    {
                      "id": "0x6fdae38",
                      "kind": "ImplicitCastExpr",
                      "range": {
                        "begin": {
                          "offset": 25,
                          "col": 7,
                          "tokLen": 1
                        },
                        "end": {
                          "offset": 25,
                          "col": 7,
                          "tokLen": 1
                        }
                      },
                      "type": {
                        "qualType": "int"
                      },
                      "valueCategory": "rvalue",
                      "castKind": "LValueToRValue",
                      "inner": [
                        {
                          "id": "0x6fdadf8",
                          "kind": "DeclRefExpr",
                          "range": {
                            "begin": {
                              "offset": 25,
                              "col": 7,
                              "tokLen": 1
                            },
                            "end": {
                              "offset": 25,
                              "col": 7,
                              "tokLen": 1
                            }
                          },
                          "type": {
                            "qualType": "int"
                          },
                          "valueCategory": "lvalue",
                          "referencedDecl": {
                            "id": "0x6fdac30",
                            "kind": "ParmVarDecl",
                            "name": "n",
                            "type": {
                              "qualType": "int"
                            }
                          }
                        }
                      ]
                    },
                    {
                      "id": "0x6fdae18",
                      "kind": "IntegerLiteral",
                      "range": {
                        "begin": {
                          "offset": 30,
                          "col": 12,
                          "tokLen": 1
                        },
                        "end": {
                          "offset": 30,
                          "col": 12,
                          "tokLen": 1
                        }
                      },
                      "type": {
                        "qualType": "int"
                      },
                      "valueCategory": "rvalue",
                      "value": "0"
                    }
                  ]
                },
                {
                  "id": "0x6fdaea0",
                  "kind": "CompoundStmt",
                  "range": {
                    "begin": {
                      "offset": 33,
                      "col": 15,
                      "tokLen": 1
                    },
                    "end": {
                      "offset": 51,
                      "line": 4,
                      "col": 3,
                      "tokLen": 1
                    }
                  },
                  "inner": [
                    {
                      "id": "0x6fdae90",
                      "kind": "ReturnStmt",
                      "range": {
                        "begin": {
                          "offset": 39,
                          "line": 3,
                          "col": 5,
                          "tokLen": 6
                        },
                        "end": {
                          "offset": 46,
                          "col": 12,
                          "tokLen": 1
                        }
                      },
                      "inner": [
                        {
                          "id": "0x6fdae70",
                          "kind": "IntegerLiteral",
                          "range": {
                            "begin": {
                              "offset": 46,
                              "col": 12,
                              "tokLen": 1
                            },
                            "end": {
                              "offset": 46,
                              "col": 12,
                              "tokLen": 1
                            }
                          },
                          "type": {
                            "qualType": "int"
                          },
                          "valueCategory": "rvalue",
                          "value": "1"
                        }
                      ]
                    }
                  ]
                },
                {
                  "id": "0x6fdb028",
                  "kind": "CompoundStmt",
                  "range": {
                    "begin": {
                      "offset": 58,
                      "line": 4,
                      "col": 10,
                      "tokLen": 1
                    },
                    "end": {
                      "offset": 86,
                      "line": 6,
                      "col": 3,
                      "tokLen": 1
                    }
                  },
                  "inner": [
                    {
                      "id": "0x6fdb018",
                      "kind": "ReturnStmt",
                      "range": {
                        "begin": {
                          "offset": 64,
                          "line": 5,
                          "col": 5,
                          "tokLen": 6
                        },
                        "end": {
                          "offset": 81,
                          "col": 22,
                          "tokLen": 1
                        }
                      },
                      "inner": [
                        {
                          "id": "0x6fdaff8",
                          "kind": "BinaryOperator",
                          "range": {
                            "begin": {
                              "offset": 71,
                              "col": 12,
                              "tokLen": 1
                            },
                            "end": {
                              "offset": 81,
                              "col": 22,
                              "tokLen": 1
                            }
                          },
                          "type": {
                            "qualType": "int"
                          },
                          "valueCategory": "rvalue",
                          "opcode": "*",
                          "inner": [
                            {
                              "id": "0x6fdafe0",
                              "kind": "ImplicitCastExpr",
                              "range": {
                                "begin": {
                                  "offset": 71,
                                  "col": 12,
                                  "tokLen": 1
                                },
                                "end": {
                                  "offset": 71,
                                  "col": 12,
                                  "tokLen": 1
                                }
                              },
                              "type": {
                                "qualType": "int"
                              },
                              "valueCategory": "rvalue",
                              "castKind": "LValueToRValue",
                              "inner": [
                                {
                                  "id": "0x6fdaeb8",
                                  "kind": "DeclRefExpr",
                                  "range": {
                                    "begin": {
                                      "offset": 71,
                                      "col": 12,
                                      "tokLen": 1
                                    },
                                    "end": {
                                      "offset": 71,
                                      "col": 12,
                                      "tokLen": 1
                                    }
                                  },
                                  "type": {
                                    "qualType": "int"
                                  },
                                  "valueCategory": "lvalue",
                                  "referencedDecl": {
                                    "id": "0x6fdac30",
                                    "kind": "ParmVarDecl",
                                    "name": "n",
                                    "type": {
                                      "qualType": "int"
                                    }
                                  }
                                }
                              ]
                            },
                            {
                              "id": "0x6fdafb8",
                              "kind": "CallExpr",
                              "range": {
                                "begin": {
                                  "offset": 73,
                                  "col": 14,
                                  "tokLen": 4
                                },
                                "end": {
                                  "offset": 81,
                                  "col": 22,
                                  "tokLen": 1
                                }
                              },
                              "type": {
                                "qualType": "int"
                              },
                              "valueCategory": "rvalue",
                              "inner": [
                                {
                                  "id": "0x6fdafa0",
                                  "kind": "ImplicitCastExpr",
                                  "range": {
                                    "begin": {
                                      "offset": 73,
                                      "col": 14,
                                      "tokLen": 4
                                    },
                                    "end": {
                                      "offset": 73,
                                      "col": 14,
                                      "tokLen": 4
                                    }
                                  },
                                  "type": {
                                    "qualType": "int (*)(int)"
                                  },
                                  "valueCategory": "rvalue",
                                  "castKind": "FunctionToPointerDecay",
                                  "inner": [
                                    {
                                      "id": "0x6fdaed8",
                                      "kind": "DeclRefExpr",
                                      "range": {
                                        "begin": {
                                          "offset": 73,
                                          "col": 14,
                                          "tokLen": 4
                                        },
                                        "end": {
                                          "offset": 73,
                                          "col": 14,
                                          "tokLen": 4
                                        }
                                      },
                                      "type": {
                                        "qualType": "int (int)"
                                      },
                                      "valueCategory": "rvalue",
                                      "referencedDecl": {
                                        "id": "0x6fdad08",
                                        "kind": "FunctionDecl",
                                        "name": "fact",
                                        "type": {
                                          "qualType": "int (int)"
                                        }
                                      }
                                    }
                                  ]
                                },
                                {
                                  "id": "0x6fdaf50",
                                  "kind": "BinaryOperator",
                                  "range": {
                                    "begin": {
                                      "offset": 78,
                                      "col": 19,
                                      "tokLen": 1
                                    },
                                    "end": {
                                      "offset": 80,
                                      "col": 21,
                                      "tokLen": 1
                                    }
                                  },
                                  "type": {
                                    "qualType": "int"
                                  },
                                  "valueCategory": "rvalue",
                                  "opcode": "-",
                                  "inner": [
                                    {
                                      "id": "0x6fdaf38",
                                      "kind": "ImplicitCastExpr",
                                      "range": {
                                        "begin": {
                                          "offset": 78,
                                          "col": 19,
                                          "tokLen": 1
                                        },
                                        "end": {
                                          "offset": 78,
                                          "col": 19,
                                          "tokLen": 1
                                        }
                                      },
                                      "type": {
                                        "qualType": "int"
                                      },
                                      "valueCategory": "rvalue",
                                      "castKind": "LValueToRValue",
                                      "inner": [
                                        {
                                          "id": "0x6fdaef8",
                                          "kind": "DeclRefExpr",
                                          "range": {
                                            "begin": {
                                              "offset": 78,
                                              "col": 19,
                                              "tokLen": 1
                                            },
                                            "end": {
                                              "offset": 78,
                                              "col": 19,
                                              "tokLen": 1
                                            }
                                          },
                                          "type": {
                                            "qualType": "int"
                                          },
                                          "valueCategory": "lvalue",
                                          "referencedDecl": {
                                            "id": "0x6fdac30",
                                            "kind": "ParmVarDecl",
                                            "name": "n",
                                            "type": {
                                              "qualType": "int"
                                            }
                                          }
                                        }
                                      ]
                                    },
                                    {
                                      "id": "0x6fdaf18",
                                      "kind": "IntegerLiteral",
                                      "range": {
                                        "begin": {
                                          "offset": 80,
                                          "col": 21,
                                          "tokLen": 1
                                        },
                                        "end": {
                                          "offset": 80,
                                          "col": 21,
                                          "tokLen": 1
                                        }
                                      },
                                      "type": {
                                        "qualType": "int"
                                      },
                                      "valueCategory": "rvalue",
                                      "value": "1"
                                    }
                                  ]
                                }
                              ]
                            }
                          ]
                        }
                      ]
                    }
                  ]
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

Filtering an object's fields

I've got the following JSON:

{
    "version": 3,
    "modules": [
        {
            "resources": {
                "cloudflare_page_rule.ajax": {
                    "type": "cloudflare_page_rule",
                    "primary": {
                        "id": "34f7cdc21665121fb83ef87347833f7c"
                    }
                }
            }
        }
    ]
}

In the original there is multiple resources.
I want to extract all IDs of resources that are of type cloudflare_page_rule.

I can use the following query to extract the type of a resource above:

$.modules[0].resources.'cloudflare_page_rule.ajax'.type

but filtering the object by the type property being set to a certain value or even set at all doesn't quite work:

$.modules[0].resources.*[?(@.type)]
$.modules[0].resources.'cloudflare_page_rule.ajax'[?(@.type=='cloudflare_page_rule')]
$.modules[0].resources.'cloudflare_page_rule.ajax'[?(@.type)]

These queries all yield no results.

Is that an error on my side or a bug in the library?

Deprecation warning due to invalid escape sequences in Python 3.8

Deprecation warnings are raised due to invalid escape sequences in Python 3.8 . Below is a log of the warnings raised during compiling all the python files. Using raw strings or escaping them will fix this issue.

find . -iname '*.py'  | xargs -P 4 -I{} python -Walways -m py_compile {}

./jsonpath_ng/ext/string.py:18: DeprecationWarning: invalid escape sequence \(
  SUB = re.compile("sub\(/(.*)/,\s+(.*)\)")
./jsonpath_ng/ext/string.py:19: DeprecationWarning: invalid escape sequence \(
  SPLIT = re.compile("split\((.),\s+(\d+),\s+(\d+|-1)\)")
./tests/test_jsonpath_rw_ext.py:129: DeprecationWarning: invalid escape sequence \c
  ('sort2', dict(string='objects[\cat]',
./tests/test_jsonpath_rw_ext.py:132: DeprecationWarning: invalid escape sequence \c
  ('sort2_indexed', dict(string='objects[\cat][-1].cat',
./tests/test_jsonpath_rw_ext.py:136: DeprecationWarning: invalid escape sequence \c
  ('sort3', dict(string='objects[/cow,\cat]',
./tests/test_jsonpath_rw_ext.py:145: DeprecationWarning: invalid escape sequence \c
  ('sort3_indexed', dict(string='objects[/cow,\cat][0].cat',

Project status?

I see quite a few issues/pr's outstanding. Is this project maintained?

(string) key values that contain digits parser error - Exception: Parse error at 1:19 near token 41 (NUMBER)

Test script:

import jsonpath_ng

tmp_data = {
    "Something":
        [
            {
            "Else":
                {
                "41":
                    {
                        "URL": "http://google.com"
                    }
                }
            }
        ]
}

jsonpath_expr = jsonpath_ng.parse('$..URL')
#import pdb ; pdb.set_trace()
for match in jsonpath_expr.find(tmp_data):
    print('%s %s %s'% (match.full_path, match.path, match.value))

match_str = str(match.full_path)
print(match_str)  # expecting: Something.[0].Else.41.URL

jsonpath_expr = jsonpath_ng.parse('Something.[0].Else.*.URL')
for match in jsonpath_expr.find(tmp_data):
    print('%s %s %s'% (match.full_path, match.path, match.value))

jsonpath_expr = jsonpath_ng.parse('Something.[0].Else.41.URL')  # Exception: Parse error at 1:19 near token 41 (NUMBER)
for match in jsonpath_expr.find(tmp_data):
    print('%s %s %s'% (match.full_path, match.path, match.value))

Fails with:

(py3jsonpath) C:\code\py\jsonpath_demo>python update_problem.py
Something.[0].Else.41.URL URL http://google.com
Something.[0].Else.41.URL
Something.[0].Else.41.URL URL http://google.com
Traceback (most recent call last):
  File "update_problem.py", line 31, in <module>
    jsonpath_expr = jsonpath_ng.parse('Something.[0].Else.41.URL')  # Exception: Parse error at 1:19 near token 41 (NUMBER)
  File "C:\code\py\jsonpath_demo\py3jsonpath\lib\site-packages\jsonpath_ng\parser.py", line 14, in parse
    return JsonPathParser().parse(string)
  File "C:\code\py\jsonpath_demo\py3jsonpath\lib\site-packages\jsonpath_ng\parser.py", line 32, in parse
    return self.parse_token_stream(lexer.tokenize(string))
  File "C:\code\py\jsonpath_demo\py3jsonpath\lib\site-packages\jsonpath_ng\parser.py", line 55, in parse_token_stream
    return new_parser.parse(lexer = IteratorToTokenStream(token_iterator))
  File "C:\code\py\jsonpath_demo\py3jsonpath\lib\site-packages\ply\yacc.py", line 333, in parse
    return self.parseopt_notrack(input, lexer, debug, tracking, tokenfunc)
  File "C:\code\py\jsonpath_demo\py3jsonpath\lib\site-packages\ply\yacc.py", line 1201, in parseopt_notrack
    tok = call_errorfunc(self.errorfunc, errtoken, self)
  File "C:\code\py\jsonpath_demo\py3jsonpath\lib\site-packages\ply\yacc.py", line 192, in call_errorfunc
    r = errorfunc(token)
  File "C:\code\py\jsonpath_demo\py3jsonpath\lib\site-packages\jsonpath_ng\parser.py", line 69, in p_error
    raise Exception('Parse error at %s:%s near token %s (%s)' % (t.lineno, t.col, t.value, t.type))
Exception: Parse error at 1:19 near token 41 (NUMBER)

[Issue] Cannot filter an element that has a null field value

JSON Example

{
  "firstName": "John",
  "lastName" : "doe",
  "age"      : 26,
  "address"  : {
    "streetAddress": "naist street",
    "city"         : "Nara",
    "postalCode"   : "630-0192"
  },
  "phoneNumbers": [
    {
      "type"  : "iPhone",
      "number": "0123-4567-8888",
      "carrier": "at&t"
    },
    {
      "type"  : "home",
      "number": "0123-4567-8910",
      "carrier": null
    }
  ]
}

I can use $.phoneNumbers[?(@.carrier==null)].type on https://jsonpath.com/ but the jsonpath-ng library always return an empty list

Unexpected character '~'

When I am trying to obtain the keys from this json
json = { "ens33": { "flags": "4163<UP,BROADCAST,RUNNING,MULTICAST>", "mtu": "1500", "inet": "10.2.10.36", "netmask": "255.255.255.0", "broadcast": "10.2.10.255" }, "lo": { "flags": "73<UP,LOOPBACK,RUNNING>", "mtu": "65536", "inet": "127.0.0.1", "netmask": "255.0.0.0", "inet6": "::1", "prefixlen": "128", } }

with the following code

keys = parse("$.*~").find(json)
Expected result
["ens33", "lo"]

But I have this error
**jsonpath_ng.lexer.JsonPathLexerError: Error on line 1, col 3: Unexpected character: ~**

some workaround to obtain the keys from json?

Thanks in advance

jsonpath division error

Hi,
Great Library!
But unfortunately, I get an error trying to use the division operator.

Example:
from jsonpath_ng.ext import parse
jsonpath_expr = parse("A / 80", debug=True)

Traceback (most recent call last):
File "test_jsonpath.py", line 2, in
jsonpath_expr = parse("A / 80", debug=True)
File "/usr/local/lib/python2.7/site-packages/jsonpath_ng/ext/parser.py", line 170, in parse
return ExtentedJsonPathParser(debug=debug).parse(path)
File "/usr/local/lib/python2.7/site-packages/jsonpath_ng/parser.py", line 32, in parse
return self.parse_token_stream(lexer.tokenize(string))
File "/usr/local/lib/python2.7/site-packages/jsonpath_ng/parser.py", line 55, in parse_token_stream
return new_parser.parse(lexer = IteratorToTokenStream(token_iterator))
File "/usr/local/lib/python2.7/site-packages/ply/yacc.py", line 333, in parse
return self.parseopt_notrack(input, lexer, debug, tracking, tokenfunc)
File "/usr/local/lib/python2.7/site-packages/ply/yacc.py", line 1201, in parseopt_notrack
tok = call_errorfunc(self.errorfunc, errtoken, self)
File "/usr/local/lib/python2.7/site-packages/ply/yacc.py", line 192, in call_errorfunc
r = errorfunc(token)
File "/usr/local/lib/python2.7/site-packages/jsonpath_ng/parser.py", line 69, in p_error
raise Exception('Parse error at %s:%s near token %s (%s)' % (t.lineno, t.col, t.value, t.type))
Exception: Parse error at 1:2 near token / (SORT_DIRECTION)

Am I using it wrong?
I tried to debug the code, but I couldnt figure out why it does not work.

Thanks a lot for any hints!
Best,
Till

Updating a json object fails if the value of a key is boolean

Related to #47

Steps to reproduce:

from jsonpath_ng import parse
selector = '$.*.number'
data = {
    'foo': ['abc', 'def'],
    'bar': {'number': 123456},
    'boolean': True
}
val = '98765'
expr = parse(selector)
expr.update(data, val)

Expected

'number' field to be updated to '98765'.

Observed

  File "/home/andrew/test.py", line 12, in <module>
    expr.update(data, val)
  File "/home/andrew/jsonpath_ng/jsonpath.py", line 261, in update
    self.right.update(datum.value, val)
  File "/home/andrew/jsonpath_ng/jsonpath.py", line 526, in update
    if field in data:
TypeError: argument of type 'bool' is not iterable

p_expressions_or is not implemented

Hello,

I would like to ask if there are plans to implement a p_expression_or method in the parser.y

I can see that and (&) is implemented:

def p_expressions_and(self, p):

        "expressions : expressions '&' expressions"

        # TODO(sileht): implements '|'

        p[0] = p[1] + p[3]

however there is no "or" capability implemented and we cannot filter paths using expressions like:
[?a > 1 or b >1]. Currently only [@A>1 & b>1] is supported.

Readme examples do not cover update and filter

The examples in the READMEdo not cover the update and filter functions. There is no documentation on these leading to puzzles as how to use them - see #42 - and many readers may not even realize the functionality is even there. I propose the following is added:

# Modifying values matching path
>>> jsonpath_expr.update( {'foo': [{'baz': 1}, {'baz': 2}]}, 3)
{'foo': [{'baz': 3}, {'baz': 3}]}

# Modifying one of the values matching path
>>> matches = jsonpath_expr.find({'foo': [{'baz': 1}, {'baz': 2}]})
>>> matches[0].full_path.update( {'foo': [{'baz': 1}, {'baz': 2}]}, 3)
{'foo': [{'baz': 3}, {'baz': 2}]}

# Removing all values matching path
>>> jsonpath_expr.filter(lambda d: True, {'foo': [{'baz': 1}, {'baz': 2}]})
{'foo': [{}, {}]}

# Removing values containing particular data matching path
>>> jsonpath_expr.filter(lambda d: d == 2, {'foo': [{'baz': 1}, {'baz': 2}]})
{'foo': [{'baz': 1}, {}]}

Filters not implemented

Hi, thanks for your work on making a standards compliant jsonpath library for Python.

I think this library (and python-jsonpath-rw) might have a bug or are missing this part of the spec:

using the symbol '@' for the current object. Filter expressions are supported via the syntax ?() as in

$.store.book[?(@.price < 10)].title

...

$..book[?(@.isbn)] | filter all books with isbn number
$..book[?(@.price<10)] | filter all books cheapier than 10

I'm trying to filter an array by a string. My query, `$.phoneNumbers[?(@.type=='home')].number, works fine on the jsonpath.com testing tool, but I can't get it working using jsonpath-ng. It fails to parse the query, complaining about the question mark.

Example code:

import json
from jsonpath_ng import jsonpath, parse

data = '''
{
  "firstName": "John",
  "lastName" : "doe",
  "age"      : 26,
  "address"  : {
    "streetAddress": "naist street",
    "city"         : "Nara",
    "postalCode"   : "630-0192"
  },
  "phoneNumbers": [
    {
      "type"  : "iPhone",
      "number": "0123-4567-8888"
    },
    {
      "type"  : "home",
      "number": "0123-4567-8910"
    }
  ]
}
'''
data = json.loads(data)
jsonpath_expr = parse(data)
jsonpath_query = "$.phoneNumbers[?(@.type=='home')].number"

Exception: JsonPathLexerError: Error on line 1, col 15: Unexpected character: ?

I'm trying this with jsonpath-ng>=1.4.3

Any ideas? Am I doing something wrong?

Feature request

Looking for a way to compare current object to another object:

$.objects[1].some_other_thing[?(@.some_field != $.objects[0].some_thing.some_field)]

it would also be nice to be able to compare arrays, if that's at all possible:

$.objects[?(@.some_other_thing != @.some_thing)].some_field

Example where some_field is different:

[ { "objects": [ { "some_thing": { "some_field": 1 } }, { "some_thing": { "some_field": 2 } } ] } ]

Result:

'some_field': 1, 'some_field': 2

Example where some_field is the same:

[ { "objects": [ { "some_thing": { "some_field": 1 } }, { "some_other_thing": { "some_field": 1 } } ] } ]

Result:

'some_field': 1,

This may be out of the scope of this project, or it's possible there's already a way to do this.
Attempting to try this caused errors around $ and @ in the filter after the !=:

$.objects[1].some_other_thing[?(@.some_field != $.objects[0].some_thing.some_field)]

and

$.objects[?(@.some_other_thing != @.some_thing)].some_field

Parsing bug in version 1.5.1 on recursive searches for dicts containing a specified key

Steps to reproduce:

from jsonpath_ng.ext import parse
import json
dat = json.loads('[{"specialKey": 5, "value": 8}, {"otherKey": 87}, [{"specialKey": 123, "value": 12}], {"olderStuff": [{"specialKey": 124, "value": 88}]}]')
jsonpath_expr = parse('$..[?(@.specialKey)]')
payloads = jsonpath_expr.find(dat)

Expected

Recursively search for dicts with a key of specialKey & return:

[
  {"specialKey": 5, "value": 8},
  {"specialKey": 123, "value": 12},
  {"specialKey": 124, "value": 88}
]

Observed

Parsing error from parse call:

In [101]: jsonpath_expr = parse('$..[?(@.StreamId)]')                                                                                                  
---------------------------------------------------------------------------
Exception                                 Traceback (most recent call last)
<ipython-input-101-a74f026fcfab> in <module>
----> 1 jsonpath_expr = parse('$..[?(@.StreamId)]')

~/tmp/venv/lib/python3.7/site-packages/jsonpath_ng/ext/parser.py in parse(path, debug)
    170 
    171 def parse(path, debug=False):
--> 172     return ExtentedJsonPathParser(debug=debug).parse(path)

~/tmp/venv/lib/python3.7/site-packages/jsonpath_ng/parser.py in parse(self, string, lexer)
     30     def parse(self, string, lexer = None):
     31         lexer = lexer or self.lexer_class()
---> 32         return self.parse_token_stream(lexer.tokenize(string))
     33 
     34     def parse_token_stream(self, token_iterator, start_symbol='jsonpath'):

~/tmp/venv/lib/python3.7/site-packages/jsonpath_ng/parser.py in parse_token_stream(self, token_iterator, start_symbol)
     53                                    errorlog = logger)
     54 
---> 55         return new_parser.parse(lexer = IteratorToTokenStream(token_iterator))
     56 
     57     # ===================== PLY Parser specification =====================

~/tmp/venv/lib/python3.7/site-packages/ply/yacc.py in parse(self, input, lexer, debug, tracking, tokenfunc)
    331             return self.parseopt(input, lexer, debug, tracking, tokenfunc)
    332         else:
--> 333             return self.parseopt_notrack(input, lexer, debug, tracking, tokenfunc)
    334 
    335 

~/tmp/venv/lib/python3.7/site-packages/ply/yacc.py in parseopt_notrack(self, input, lexer, debug, tracking, tokenfunc)
   1199                             errtoken.lexer = lexer
   1200                         self.state = state
-> 1201                         tok = call_errorfunc(self.errorfunc, errtoken, self)
   1202                         if self.errorok:
   1203                             # User must have done some kind of panic

~/tmp/venv/lib/python3.7/site-packages/ply/yacc.py in call_errorfunc(errorfunc, token, parser)
    190     _token = parser.token
    191     _restart = parser.restart
--> 192     r = errorfunc(token)
    193     try:
    194         del _errok, _token, _restart

~/tmp/venv/lib/python3.7/site-packages/jsonpath_ng/parser.py in p_error(self, t)
     67 
     68     def p_error(self, t):
---> 69         raise Exception('Parse error at %s:%s near token %s (%s)' % (t.lineno, t.col, t.value, t.type))
     70 
     71     def p_jsonpath_binop(self, p):

Exception: Parse error at 1:4 near token ? (?)

Notes

This jsonpath expression parses and works as expected when I tested on https://jsonpath.com/
I get a similar failure with jsonpath_rw, the next-best jsonpath library for python.

Unexpected character '#'

when i run code belows

from jsonpath_ng import jsonpath, parse
dct = {'A#B': [1,2,3]}
pt = parse('A#B')
print pt.find(dct)

it gives me JsonPathLexerError: Error on line 1, col 1: Unexpected character: #

so, is there any method or extra setting i can solve this problem?

in addition, i want to know which characters will meet the same problem?

thanks.

entry-point jsonpath.py

Hello,
There is an import conflict of jsonpath.
I found that during installation jsonpath-ng defines an entry-point in bin/jsonpath.py and this is problematic since there is another python library named jsonpath here

image

The conflict arises when we try to import jsonpath (import jsonpath or from jsonpath import jsonpath) and instead of importing jsonpath of the stated library , it is the jsonpath of the jsonpath-ng that is imported.

Is there anyway to avoid this conflict ? Maybe some coordinations between maintainers ?

Feel free to ask for more details.

Filter leaves, and return leaves, not parent of leaf

I can use the expression $..*[?(@[*] = "\r\n")] to for example search for any instances of the exact string "\r\n" in a tree structure. This works but it returns the "parents" of the leaf "\r\n" nodes, i.e. the dict or list containing the string.

Is there any way to get the strings/leaves themselves as results? This isn't useful for the case where you're matching an exact string, but would be useful for regexes for example.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.