Home > OS >  How dynamically set value to complex json content, receiving key (or key path) and value as paramete
How dynamically set value to complex json content, receiving key (or key path) and value as paramete

Time:11-13

As json example, I have this:

{
  "id": "foo",
  "items": [
    {
      "id": "aaa",
      "colour": "blue"
    },
    { 
      "id": "bbb",
      "colour": "red",
    }
  ]
}

Once json is read in Python into a dictionary:

What I want to do is a function that receives field to fill and value to be set For example:

func(json_dict, 'items[0].colour', 'green')

or

func(json_dict, 'id', 'bar')

And the challenge is to do this for whatever json with whatever deep I have. But I don't know how dynamically I can do:

json_dict['items'][0]['colour'] = 'green'
json_dict['id'] = 'bar'

CodePudding user response:

Parse the path and define an array of keys to traverse. Then recursively find the last key and set the value.

def set_val(json_obj, path, val):
    path_arr = []
    for _, k, i in re.findall(r'((\w )(\[\d \])?\.?)', path):
        path_arr.append(k)
        if i: path_arr.append(int(i[1:-1]))
    def _get(c_path, curr=json_obj):
        if len(c_path) == 1: curr[c_path[0]] = val
        else: _get(c_path[1:], curr[c_path[0]])
    _get(path_arr)

my_json = {'a': {'b': [1, 2, {'x': 2}]}}

set_val(my_json, 'a.b[2].x', 5)

print(my_json)  # ==> {'a': {'b': [1, 2, {'x': 5}]}}

CodePudding user response:

Here's one alternative to a regex approach. However, a minor disadvantage is you can't use a 'a.b[2].x' syntax anymore.

from typing import Tuple, Union


def set_val(d, path, val):
    *paths, last_path = _get_dot_path(path)
    path_dict = d

    try:
        for i, p in enumerate(paths):
            path_dict = path_dict[p]

        path_dict[last_path] = val

    except (KeyError, IndexError, TypeError):
        # TODO use variables i and p, if needed
        raise


def _get_dot_path(path: str) -> Tuple[Union[str, int], ...]:
    return tuple([int(s) if s.lstrip('-').isdecimal() else s
                 for s in path.split('.')])


my_json = {'a': {'b': [1, 2, {'x': 2}]}}

set_val(my_json, 'a.b.2.x', 5)

print(my_json)  # {'a': {'b': [1, 2, {'x': 5}]}}
  • Related