ネストされたpython辞書に対してXPathタイプのクエリを定義する方法はありますか?.
このようなもの:
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")
[編集2016]この質問と受け入れられた答えは古くからあります。新しい回答は、元の回答よりもうまく機能する場合があります。しかし、私はそれらをテストしなかったので、受け入れられた答えを変更しません。
正確には美しくありませんが、次のように使用できます
def xpath_get(mydict, path):
elem = mydict
try:
for x in path.strip("/").split("/"):
elem = elem.get(x)
except:
pass
return elem
もちろん、これはインデックスのようなxpathをサポートしていません...もちろん/
キートラップunutbuが表示されました。
私が識別できた最高のライブラリの1つは、さらに非常に活発に開発されており、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]
これを行う簡単な方法があります。
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'}を保持します。
[〜#〜] jsonpath [〜#〜] 構文をサポートする新しい jsonpath-rw ライブラリがありますが、pythondictionariesおよびarrays、ご希望どおり。
したがって、最初の例は次のようになります。
from jsonpath_rw import parse
print( parse('$.morefoo.morebar').find(foo) )
そして2番目:
print( parse("$.morefoo[0].morebar.bacon").find(foo) )
PS:辞書をサポートするもう1つのより単純なライブラリは python-json-pointer で、よりXPathに似た構文です。
簡潔さがあなたのファンシーである場合:
def xpath(root, path, sch='/'):
return reduce(lambda acc, nxt: acc[nxt],
[int(x) if x.isdigit() else x for x in path.split(sch)],
root)
もちろん、only dictがある場合は、より簡単です。
def xpath(root, path, sch='/'):
return reduce(lambda acc, nxt: acc[nxt],
path.split(sch),
root)
パス仕様にエラーがあれば幸いです;-)
JMESPath を使用できます。これは、JSONのクエリ言語であり、 python実装 があります。
import jmespath # pip install jmespath
data = {'root': {'section': {'item1': 'value1', 'item2': 'value2'}}}
jmespath.search('root.section.item2', data)
Out[42]: 'value2'
Jmespathクエリ構文と実際の例: http://jmespath.org/tutorial.html
別のオプションは、 dicttoxml のようなものを使用して辞書をXMLに変換してから、通常のXPath式を使用することです。 lxml またはその他のライブラリを使用します。
from dicttoxml import dicttoxml # pip install dicttoxml
from lxml import etree # pip install lxml
data = {'root': {'section': {'item1': 'value1', 'item2': 'value2'}}}
xml_data = dicttoxml(data, attr_type=False)
Out[43]: b'<?xml version="1.0" encoding="UTF-8" ?><root><root><section><item1>value1</item1><item2>value2</item2></section></root></root>'
tree = etree.fromstring(xml_data)
tree.xpath('//item2/text()')
Out[44]: ['value2']
XPathのようなセレクターがどのように機能するかについては、さらに作業を行う必要があります。 '/'
は有効な辞書キーなので、どのように
foo={'/':{'/':'eggs'},'//':'ham'}
扱われる?
foo.select("///")
あいまいになります。
( 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")
XPathパターンのようにクエリする理由はありますか?あなたの質問へのコメント者が示唆したように、それは単なる辞書なので、ネストした方法で要素にアクセスできます。また、データがJSON形式であることを考慮して、simplejsonモジュールを使用してデータをロードし、要素にアクセスすることもできます。
このプロジェクト [〜#〜] jsonpath [〜#〜] があります。これは、人々があなたがやろうとしていることの反対を行うのを助けようとしています(XPATHを与えられた、=で簡単にアクセスできるようにする方法) pythonオブジェクト)、より便利に思えます。
def Dict(var, *arg, **kwarg):
""" Return the value of an (imbricated) dictionnary, if all fields exist else return "" unless "default=new_value" specified as end argument
Avoid TypeError: argument of type 'NoneType' is not iterable
Ex: Dict(variable_dict, 'field1', 'field2', default = 0)
"""
for key in arg:
if isinstance(var, dict) and key and key in var: var = var[key]
else: return kwarg['default'] if kwarg and 'default' in kwarg else "" # Allow Dict(var, tvdbid).isdigit() for example
return kwarg['default'] if var in (None, '', 'N/A', 'null') and kwarg and 'default' in kwarg else "" if var in (None, '', 'N/A', 'null') else var
foo = {
'spam':'eggs',
'morefoo': {
'bar':'soap',
'morebar': {'bacon' : 'foobar'}
}
}
print Dict(foo, 'morefoo', 'morebar')
print Dict(foo, 'morefoo', 'morebar', default=None)
辞書のリストに追加することさえできるSaveDict(value、var、* arg)関数があります...