Как динамически получить путь JSON и изменить значение поля JSON с помощью пути - PullRequest
2 голосов
/ 14 июня 2019

У меня много json-файлов, их структура различна. Мне нужно каждый раз менять значение поля в json, значения других полей остаются неизменными.

Теперь я смог динамически получить код пути json следующим образом

def get_paths(source):
    paths = []
    if isinstance(source, collections.MutableMapping):  # found a dict-like structure...
        for k, v in source.items():  # iterate over it; Python 2.x: source.iteritems()
            paths.append([k])  # add the current child path
            paths += [[k] + x for x in get_paths(v)]  # get sub-paths, extend with the current
    # else, check if a list-like structure, remove if you don't want list paths included
    elif isinstance(source, collections.Sequence) and not isinstance(source, str):
        for i, v in enumerate(source):
            paths.append([i])
            paths += [[i] + x for x in get_paths(v)]  # get sub-paths, extend with the current
    return paths

Один из примеров json выглядит следующим образом, это только один из множества json:

{
    "glossary": {
        "title": "example glossary",
        "GlossDiv": {
            "title": "S",
            "GlossList": {
                "GlossEntry": {
                    "ID": "SGML",
                    "SortAs": "SGML",
                    "GlossTerm": "Standard Generalized Markup Language",
                    "Acronym": "SGML",
                    "Abbrev": "ISO 8879:1986",
                    "GlossDef": {
                        "para": "A meta-markup language, used to create markup languages...",
                        "GlossSeeAlso": [
                            "GML",
                            "XML"
                        ]
                    },
                    "GlossSee": "markup"
                }
            }
        }
    }
}

Используйте следующий код для получения путей

def loadJson():
    try:
        with open('../json/test1.json', 'r') as loadf:
            load_dict = json.load(loadf)
            return load_dict
    except Exception as e:
        raise Exception("load json fail")

t_json = loadJson()
paths = get_paths(loadJson())

Путь в этом примере следующий:

[['glossary'],
 ['glossary', 'title'],
 ['glossary', 'GlossDiv'],
 ['glossary', 'GlossDiv', 'title'],
 ['glossary', 'GlossDiv', 'GlossList'],
 ['glossary', 'GlossDiv', 'GlossList', 'GlossEntry'],
 ['glossary', 'GlossDiv', 'GlossList', 'GlossEntry', 'ID'],
 ['glossary', 'GlossDiv', 'GlossList', 'GlossEntry', 'SortAs'],
 ['glossary', 'GlossDiv', 'GlossList', 'GlossEntry', 'GlossTerm'],
 ['glossary', 'GlossDiv', 'GlossList', 'GlossEntry', 'Acronym'],
 ['glossary', 'GlossDiv', 'GlossList', 'GlossEntry', 'Abbrev'],
 ['glossary', 'GlossDiv', 'GlossList', 'GlossEntry', 'GlossDef'],
 ['glossary', 'GlossDiv', 'GlossList', 'GlossEntry', 'GlossDef', 'para'],
 ['glossary', 'GlossDiv', 'GlossList', 'GlossEntry', 'GlossDef', 'GlossSeeAlso'],
 ['glossary', 'GlossDiv', 'GlossList', 'GlossEntry', 'GlossDef', 'GlossSeeAlso', 0],
 ['glossary', 'GlossDiv', 'GlossList', 'GlossEntry', 'GlossDef', 'GlossSeeAlso', 1],
 ['glossary', 'GlossDiv', 'GlossList', 'GlossEntry', 'GlossSee']]

Вопрос теперь в том, как динамически изменить значение одного из полей по полученному пути?

Пример: я хочу изменить "title": "S" на "title": "M".

Как использовать ['glossary', 'GlossDiv', 'title'] для этого?

for path in paths:
   # How to get t_json[path[0]][path[1]][path[2]]?

Ответы [ 2 ]

1 голос
/ 14 июня 2019

Вот основная идея, как это сделать с path, который представляет собой последовательность клавиш:

from functools import reduce

# From https://stackoverflow.com/a/28225747/355230
def recursive_get(d, *keys):
    return reduce(lambda c, k: c.get(k, {}), keys, d)

t_json = loadJson()
path = ['glossary', 'GlossDiv', 'title']
sub_dict = recursive_get(t_json, *path[:-1])
sub_dict['title'] = 'M'
print(t_json)
1 голос
/ 14 июня 2019

Как получить t_json[path[0]][path[1]][path[2]]?

Простой: нам просто нужно перебрать path, применяя одну операцию индексации за раз.Это требует, чтобы мы помнили наш прогресс после каждого шага, и самый простой способ - просто повторно использовать переменную, которая прослеживает его путь по пути.Так, например:

element = t_json
for path_item in path:
    element = element[path_item]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...