Как полностью пройти сложный словарь неизвестной глубины?
Импорт из JSON
может иметь очень сложные и вложенные структуры.
Например:
{u'body': [{u'declarations': [{u'id': {u'name': u'i',
u'type': u'Identifier'},
u'init': {u'type': u'Literal', u'value': 2},
u'type': u'VariableDeclarator'}],
u'kind': u'var',
u'type': u'VariableDeclaration'},
{u'declarations': [{u'id': {u'name': u'j',
u'type': u'Identifier'},
u'init': {u'type': u'Literal', u'value': 4},
u'type': u'VariableDeclarator'}],
u'kind': u'var',
u'type': u'VariableDeclaration'},
{u'declarations': [{u'id': {u'name': u'answer',
u'type': u'Identifier'},
u'init': {u'left': {u'name': u'i',
u'type': u'Identifier'},
u'operator': u'*',
u'right': {u'name': u'j',
u'type': u'Identifier'},
u'type': u'BinaryExpression'},
u'type': u'VariableDeclarator'}],
u'kind': u'var',
u'type': u'VariableDeclaration'}],
u'type': u'Program'}
Каков рекомендуемый способ перемещения сложных структур, подобных описанным выше?
Помимо нескольких списков есть в основном словари, структура может стать еще более измененной, поэтому мне нужно общее решение.
Ответы
Ответ 1
Вы можете использовать рекурсивный генератор для преобразования вашего словаря в плоские списки.
def dict_generator(indict, pre=None):
pre = pre[:] if pre else []
if isinstance(indict, dict):
for key, value in indict.items():
if isinstance(value, dict):
for d in dict_generator(value, [key] + pre):
yield d
elif isinstance(value, list) or isinstance(value, tuple):
for v in value:
for d in dict_generator(v, [key] + pre):
yield d
else:
yield pre + [key, value]
else:
yield indict
Он возвращает
[u'body', u'kind', u'var']
[u'init', u'declarations', u'body', u'type', u'Literal']
[u'init', u'declarations', u'body', u'value', 2]
[u'declarations', u'body', u'type', u'VariableDeclarator']
[u'id', u'declarations', u'body', u'type', u'Identifier']
[u'id', u'declarations', u'body', u'name', u'i']
[u'body', u'type', u'VariableDeclaration']
[u'body', u'kind', u'var']
[u'init', u'declarations', u'body', u'type', u'Literal']
[u'init', u'declarations', u'body', u'value', 4]
[u'declarations', u'body', u'type', u'VariableDeclarator']
[u'id', u'declarations', u'body', u'type', u'Identifier']
[u'id', u'declarations', u'body', u'name', u'j']
[u'body', u'type', u'VariableDeclaration']
[u'body', u'kind', u'var']
[u'init', u'declarations', u'body', u'operator', u'*']
[u'right', u'init', u'declarations', u'body', u'type', u'Identifier']
[u'right', u'init', u'declarations', u'body', u'name', u'j']
[u'init', u'declarations', u'body', u'type', u'BinaryExpression']
[u'left', u'init', u'declarations', u'body', u'type', u'Identifier']
[u'left', u'init', u'declarations', u'body', u'name', u'i']
[u'declarations', u'body', u'type', u'VariableDeclarator']
[u'id', u'declarations', u'body', u'type', u'Identifier']
[u'id', u'declarations', u'body', u'name', u'answer']
[u'body', u'type', u'VariableDeclaration']
[u'type', u'Program']
Ответ 2
Если вам нужно только ходить по словарю, я бы предложил использовать рекурсивную функцию walk
, которая принимает словарь, а затем рекурсивно просматривает его элементы. Что-то вроде этого:
def walk(node):
for key, item in node.items():
if item is a collection:
walk(item)
else:
It is a leaf, do your thing
Если вы также хотите искать элементы или запрашивать несколько элементов, которые соответствуют определенным критериям, посмотрите модуль jsonpath.
Ответ 3
Вместо написания вашего собственного анализатора, в зависимости от задачи, вы можете расширить кодеры и декодеры из стандартного библиотечного модуля json
.
Я рекомендую это особенно, если вам нужно кодировать объекты, принадлежащие пользовательским классам, в json. Если вам нужно выполнить какую-либо операцию, которая может быть выполнена также для строкового представления json, рассмотрите также итерацию JSONEncoder(). Iterencode
Обе ссылки: http://docs.python.org/2/library/json.html#encoders-and-decoders.
Ответ 4
Если вы знаете значение данных, вы можете создать функцию parse
, чтобы превратить вложенные контейнеры в дерево объектов пользовательских типов. Затем вы использовали методы этих пользовательских объектов для выполнения всех необходимых действий с данными.
Для вашей структуры данных примера вы можете создать классы Program
, VariableDeclaration
, VariableDeclarator
, Identifier
, Literal
и BinaryExpression
, а затем использовать для этого анализатора что-то вроде этого:
def parse(d):
t = d[u"type"]
if t == u"Program":
body = [parse(block) for block in d[u"body"]]
return Program(body)
else if t == u"VariableDeclaration":
kind = d[u"kind"]
declarations = [parse(declaration) for declaration in d[u"declarations"]]
return VariableDeclaration(kind, declarations)
else if t == u"VariableDeclarator":
id = parse(d[u"id"])
init = parse(d[u"init"])
return VariableDeclarator(id, init)
else if t == u"Identifier":
return Identifier(d[u"name"])
else if t == u"Literal":
return Literal(d[u"value"])
else if t == u"BinaryExpression":
operator = d[u"operator"]
left = parse(d[u"left"])
right = parse(d[u"right"])
return BinaryExpression(operator, left, right)
else:
raise ValueError("Invalid data structure.")
Ответ 5
Может быть, может помочь:
def walk(d):
global path
for k,v in d.items():
if isinstance(v, str) or isinstance(v, int) or isinstance(v, float):
path.append(k)
print "{}={}".format(".".join(path), v)
path.pop()
elif v is None:
path.append(k)
## do something special
path.pop()
elif isinstance(v, dict):
path.append(k)
walk(v)
path.pop()
else:
print "###Type {} not recognized: {}.{}={}".format(type(v), ".".join(path),k, v)
mydict = {'Other': {'Stuff': {'Here': {'Key': 'Value'}}}, 'root1': {'address': {'country': 'Brazil', 'city': 'Sao', 'x': 'Pinheiros'}, 'surname': 'Fabiano', 'name': 'Silos', 'height': 1.9}, 'root2': {'address': {'country': 'Brazil', 'detail': {'neighbourhood': 'Central'}, 'city': 'Recife'}, 'surname': 'My', 'name': 'Friend', 'height': 1.78}}
path = []
walk(mydict)
Будет выводить вывод следующим образом:
Other.Stuff.Here.Key=Value
root1.height=1.9
root1.surname=Fabiano
root1.name=Silos
root1.address.country=Brazil
root1.address.x=Pinheiros
root1.address.city=Sao
root2.height=1.78
root2.surname=My
root2.name=Friend
root2.address.country=Brazil
root2.address.detail.neighbourhood=Central
root2.address.city=Recife
Ответ 6
Некоторое дополнение к решению выше (для обработки json, включая списки)
#!/usr/bin/env python
import json
def walk(d):
global path
for k,v in d.items():
if isinstance(v, str) or isinstance(v, int) or isinstance(v, float):
path.append(k)
print("{}={}".format(".".join(path), v))
path.pop()
elif v is None:
path.append(k)
# do something special
path.pop()
elif isinstance(v, list):
path.append(k)
for v_int in v:
walk(v_int)
path.pop()
elif isinstance(v, dict):
path.append(k)
walk(v)
path.pop()
else:
print("###Type {} not recognized: {}.{}={}".format(type(v), ".".join(path),k, v))
with open('abc.json') as f:
myjson = json.load(f)
path = []
walk(myjson)