h2non / jsonpath-ng

Finally, a JSONPath implementation for Python that aims to be standard compliant. That's all. Enjoy!
Apache License 2.0
564 stars 85 forks source link

Filter expression cannot be parsed properly #162

Open jprawiharjo opened 6 months ago

jprawiharjo commented 6 months ago

This expression can be parsed on https://jsonpath.com/ :

$..[?(@.gender=="Female")]

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

Example JSON:

{
            "students": [
                {"name": "Peter", "gender": "Male", "age": 20},
                {"name": "Mary", "gender": "Female", "age": 30},
                {"name": "Susan", "gender": "Female", "age": 40}
            ],
            "teachers": [
                {"name": "William", "gender": "Male", "age": 30},
                {"name": "John", "gender": "Male", "age": 40},
                {"name": "Lucy", "gender": "Female", "age": 50}
            ]
        }
smithumble commented 5 months ago

same

smithumble commented 5 months ago

Got it

You should use

from jsonpath_ng.ext import parse

instead of

from jsonpath_ng import parse

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

jprawiharjo commented 4 months ago

Using .ext does not solve this

e-d-n-a commented 2 months ago

There are 2 options to realize this:

Common code:

from jsonpath_ng import parse as jpe
from jsonpath_ng.ext import parse as jpx
j = {
            "students": [
                {"name": "Peter", "gender": "Male", "age": 20},
                {"name": "Mary", "gender": "Female", "age": 30},
                {"name": "Susan", "gender": "Female", "age": 40}
            ],
            "teachers": [
                {"name": "William", "gender": "Male", "age": 30},
                {"name": "John", "gender": "Male", "age": 40},
                {"name": "Lucy", "gender": "Female", "age": 50}
            ]
        }

1. Using a single expression and with same isolated output as with jsonpath.com:

jpx_female = jpx('$.*[?gender = "Female"]')
females = jpx_female.find(j)
[f.value for f in females]

Result:

[{'name': 'Mary', 'gender': 'Female', 'age': 30},
 {'name': 'Susan', 'gender': 'Female', 'age': 40},
 {'name': 'Lucy', 'gender': 'Female', 'age': 50}]

2. By using a filter on an expression for persons:

jpe_person = jpe('$.*.[*]') # or jpe('$.*[*]')
females = jpe_person.filter(lambda p: p['gender'] != 'Female', j)
females

Result:

{'students': [{'age': 30, 'gender': 'Female', 'name': 'Mary'},
              {'age': 40, 'gender': 'Female', 'name': 'Susan'}],
 'teachers': [{'age': 50, 'gender': 'Female', 'name': 'Lucy'}]}

From that filtered dict you can also return the person dicts isolated by applying the person expression:

persons = jpe_person.find(females)
[f.value for f in persons]

Result:

[{'name': 'Mary', 'gender': 'Female', 'age': 30},
 {'name': 'Susan', 'gender': 'Female', 'age': 40},
 {'name': 'Lucy', 'gender': 'Female', 'age': 50}]
e-d-n-a commented 2 months ago

I missed, that jsonpath_ng.ext.parse also supports filter expressions in a style as seen in the initial post like: $..[?(@.gender=="Female")]

Anyways with this syntax, the following (with only a instead of a .) works as well for an extended expression: `$.[?(@.gender == "Female")]` Result:

[{'name': 'Mary', 'gender': 'Female', 'age': 30},
 {'name': 'Susan', 'gender': 'Female', 'age': 40},
 {'name': 'Lucy', 'gender': 'Female', 'age': 50}]

So it's just a matter of addressing the objects within the structure correctly and using .. doesn't seem reasonable to me.

Yet, this works also (not sure it makes sense though): $..*[?(@.gender == "Female")]

Another option with different (extended) syntax is also: $.* where $[?gender = "Female"]