Xpath как запрос для вложенных словарей python
Есть ли способ определить запрос типа XPath для вложенных словарей python.
Что-то вроде этого:
foo = {
'spam':'eggs',
'morefoo': {
'bar':'soap',
'morebar': {'bacon' : 'foobar'}
}
}
print( foo.select("/morefoo/morebar") )
>> {'bacon' : 'foobar'}
Мне также нужно было выбрать вложенные списки;)
Это можно легко сделать с помощью решения @jellybean:
def xpath_get(mydict, path):
elem = mydict
try:
for x in path.strip("/").split("/"):
try:
x = int(x)
elem = elem[x]
except ValueError:
elem = elem.get(x)
except:
pass
return elem
foo = {
'spam':'eggs',
'morefoo': [{
'bar':'soap',
'morebar': {
'bacon' : {
'bla':'balbla'
}
}
},
'bla'
]
}
print xpath_get(foo, "/morefoo/0/morebar/bacon")
[EDIT 2016] Этот вопрос и принятый ответ древние. Новые ответы могут сделать работу лучше, чем исходный ответ. Однако я не тестировал их, поэтому я не буду изменять принятый ответ.
Ответы
Ответ 1
Не совсем красиво, но вы можете использовать sth как
def xpath_get(mydict, path):
elem = mydict
try:
for x in path.strip("/").split("/"):
elem = elem.get(x)
except:
pass
return elem
Это не поддерживает такие вещи, как индексы, например, индексы,... не говоря уже о том, что отмечена клавиша /
unutbu.
Ответ 2
Одна из лучших библиотек, которые я смог идентифицировать, которая, кроме того, очень активно развивается, - это извлеченный проект из boto: JMESPath, У этого есть очень сильный синтаксис, чтобы делать вещи, которые обычно берут страницы кода, чтобы выразить.
Вот несколько примеров:
search('foo | bar', {"foo": {"bar": "baz"}}) -> "baz"
search('foo[*].bar | [0]', {
"foo": [{"bar": ["first1", "second1"]},
{"bar": ["first2", "second2"]}]}) -> ["first1", "second1"]
search('foo | [0]', {"foo": [0, 1, 2]}) -> [0]
Ответ 3
Теперь есть более простой способ сделать это.
http://github.com/akesterson/dpath-python
$ easy_install dpath
>>> dpath.util.search(YOUR_DICTIONARY, "morefoo/morebar")
... сделано. Или если вам не нравится возвращать результаты в представлении (объединенный словарь, который сохраняет пути), введите их вместо:
$ easy_install dpath
>>> for (path, value) in dpath.util.search(YOUR_DICTIONARY, "morefoo/morebar", yielded=True)
... и сделано. 'value' будет содержать {'bacon': 'foobar'} в этом случае.
Ответ 4
Существует новая библиотека jsonpath-rw, поддерживающая синтаксис JSONPATH, но для словарей python и массивов, как вы хотели.
Итак, ваш первый пример:
from jsonpath_rw import parse
print( parse('$.morefoo.morebar').find(foo) )
И второе:
print( parse("$.morefoo[0].morebar.bacon").find(foo) )
PS: Альтернативная более простая библиотека, поддерживающая словари, python-json-pointer с более синтаксисом XPath.
Ответ 5
Должна быть сделана работа над тем, как будет работать селектор XPath.
'/'
является допустимым ключом словаря, поэтому как
foo={'/':{'/':'eggs'},'//':'ham'}
обрабатывается?
foo.select("///")
будет неоднозначным.
Ответ 6
Есть ли у вас какая-либо причина для запроса, как у XPath-шаблона? Как сказал комментатор на ваш вопрос, это просто словарь, поэтому вы можете получить доступ к элементам в виде гнезда. Кроме того, учитывая, что данные находятся в форме JSON, вы можете использовать модуль simplejson для его загрузки и доступа к элементам.
Этот проект JSONPATH, который пытается помочь людям сделать что-то противоположное тому, что вы намереваетесь сделать (учитывая XPATH, как чтобы сделать его легко доступным через объекты python), что кажется более полезным.
Ответ 7
Другая альтернатива (помимо предложенной jellybean) заключается в следующем:
def querydict(d, q):
keys = q.split('/')
nd = d
for k in keys:
if k == '':
continue
if k in nd:
nd = nd[k]
else:
return None
return nd
foo = {
'spam':'eggs',
'morefoo': {
'bar':'soap',
'morebar': {'bacon' : 'foobar'}
}
}
print querydict(foo, "/morefoo/morebar")