Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JSONPath.update_or_create() #72

Merged
merged 2 commits into from Jul 5, 2021
Merged

Conversation

kaapstorm
Copy link

JSONPath.update_or_create() is like JSONPath.update(), but it can also create new values, for the purpose of building out a JSON document. Here is an example based on tests in tests/test_create.py:

>>> data = {}
>>> jsonpath = parse('$.name[0].text')
>>> data = jsonpath.update_or_create(data, 'Sir Michael')
>>> data
{'name': [{'text': 'Sir Michael'}]}
>>> jsonpath = parse('$.birthDate')
>>> data = jsonpath.update_or_create(data, '1943-05-05')
>>> data
{'name': [{'text': 'Sir Michael'}],
 'birthDate': '1943-05-05'}

Creating new items is only implemented for JSONPath subclasses that make sense for this use case. For example, using Slice to create many values is not supported in this change, nor is using Filter to create items to satisfy filter criteria (although you can still use Filter to select existing items).

This is a second take at PR #71 . It preserves the signatures and behaviour of the current find() and update() methods in order to work with subclasses defined outside this codebase.

fyi @h2non @proteusvacuum

@kintarowins
Copy link

This is awesome! Thanks for creating this. How about "inserting" something before or after a path? Can this be easily done?

@kaapstorm
Copy link
Author

That is an interesting question @kintarowins ... but I'm not certain I know what you mean by "insert". Do you mean, for example, if you have an object like this ...

data = {
    "foo": [
        {"bar": "baz"},
        {"bar": "bizzle"},
    ]
}

... can you "insert" a "qux": 42 to get a result like ...

{
    "foo": [
        {"bar": "baz", "qux": 42},
        {"bar": "bizzle"}
    ]
}

...?

If that is what you mean, the answer is, "yes". You can do that like so:

from jsonpath_ng.ext import parse

jsonpath_str = 'foo[?bar="baz"].qux'
result = parse(jsonpath_str).update_or_create(data, 42)

Or do you mean something else? If so, could you give an example?

@kintarowins
Copy link

@kaapstorm that's sort of what I'm looking for but with the ability to position new items at a certain order. For example

data = {
    "foo": {
        "a": "aa",
        "c": "cc",
        "d": {
            "bar": [1, 2, 3, 5]
        }
    }
}

jsonpath_str = 'foo.B'
parse(jsonpath_str).insert(data, 1, 'BB')       # insert at position 1
jsonpath_str = 'foo.d.bar'
parse(jsonpath_str).insert(data, 3, 'FOUR')     # insert at position 3

result:

data = {
    "foo": {
        "a": "aa",
        "B": "BB",
        "c": "cc",
        "d": {
            "bar": [1, 2, 3, 'FOUR', 5]
        }
    }
}

@kaapstorm
Copy link
Author

Ah, thanks @kintarowins now I get it.

Not quite as cool as what you're proposing, but I think you could achieve the same result if you inserted the value using Python, and then updated the list's property with its new value. e.g.

data = {
    "foo": {
        "a": "aa",
        "c": "cc",
        "d": {
            "bar": [1, 2, 3, 5]
        }
    }
}

jsonpath = parse('foo.d.bar')
bar = jsonpath.find(data)[0].value
bar.insert(3, 'FOUR')
data = jsonpath.update_or_create(data, bar)

Your first example is trickier, because (new in Python 3) dictionaries keep their ordering, but I don't think there is a dictionary method to insert a key using an index. You could copy the dictionary:

jsonpath = parse('foo')
foo = jsonpath.find(data)[0].value
keys = list(foo.keys())
keys.insert(1, "B")
foo['B'] = 'BB'
new_foo = {k: foo[k] for k in keys}
data = jsonpath.update_or_create(data, new_foo)

Now data will have the value you want.

@SirLich
Copy link

SirLich commented Jul 3, 2021

Is there any reason this hasn't been merged in? It looks quite solid, and would be really useful.

The only other option is pretty ugly, sadly.

@kaapstorm
Copy link
Author

@SirLich I think it's just waiting for review and approval from @h2non

@h2non h2non merged commit 2424949 into h2non:master Jul 5, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants