2011-09-06 18 views
25

ネストされたpython辞書のXPath型クエリを定義する方法はありますか?ネストされた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") 

[EDIT 2016]この質問と受け入れられた答えは古代です。新しい回答は、元の回答よりもうまくいくかもしれません。しかし、私はそれらをテストしなかったので、私は受け入れられた答えを変更しません。

+0

なぜ 'foo ['morefoo'] ['morebar']'を使用しないのですか? – MarcoS

+3

私はしたいので:def bla(query):data.select(query) – RickyA

+0

@MarcoSパスマイクロ言語が複数の項目を返すリストでは、もっと面白いでしょう。 –

答えて

8

正確に美しくはないが、あなたは示さ/キートラップunutbuを言及しないように...これはもちろん、インデックスのようなXPathのものをサポートしていません

def xpath_get(mydict, path): 
    elem = mydict 
    try: 
     for x in path.strip("/").split("/"): 
      elem = elem.get(x) 
    except: 
     pass 

    return elem 

ようSTHを使用する場合があります。

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") 
+0

2011年には今日の選択肢がないかもしれませんが、2014年にはこのように問題を解決することはエレガントではなく、避けるべきです。 – nikolay

+8

@nikolayはちょうど推測ですか、これをよりうまく解決するソリューションはありますか? –

1

XPathのようなセレクタがどのように機能するかについては、さらに多くの作業が必要です。 '/'は有効な辞書のキーなので、どのように

foo={'/':{'/':'eggs'},'//':'ham'} 

が処理されるのでしょうか?

foo.select("///") 

はあいまいです。

+0

はい、そのためにパーサが必要です。しかし、私が求めているのはxpath _like_メソッドです。 "morefoo.morebar"は私には問題ありません。 – RickyA

+2

@RickyA: '''は、値の辞書キーでもあります。同じ問題が存在します。 'foo.select( '...')'はあいまいです。 – unutbu

1

XPathパターンのようにクエリを実行する理由はありますか?あなたの質問に対するコメント投稿者が示唆したように、それはちょうど辞書なので、ネストの方法で要素にアクセスすることができます。また、データがJSON形式であることを考慮すると、simplejsonモジュールを使用してそのデータをロードして要素にアクセスすることもできます。

このプロジェクトは、人々があなたがやろうとしていることの反対をするのを助けようとしています(XPATHを与えれば、Pythonオブジェクトから簡単にアクセスできるようにする方法)。

+0

その理由は、データとクエリを分割したいからです。私はクエリ部分で柔軟になりたい。入れ子になった方法でアクセスすると、クエリはプログラムでハードコードされます。 – RickyA

+0

@RickyA、他のコメントでは、morefoo.morebarは大丈夫です。 JSONPATHプロジェクトをチェックしましたか(ソースとテストをダウンロードして見てください)。 –

+0

私はJSONPATHを見ましたが、私の入力はtext/jsonではありません。それはネストされた辞書です。 – RickyA

1

jellybeanによって提案されたもの以外の)別の方法としてはこれです。行わ

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ライブラリがあるが、Python用

11

今これを行うための簡単な方法があります:

+0

反復ステートメントは実行されません。forステートメントの本文はありません。 – Mittenchops

10

だからあなたの第一の例では、次のようになります。

from jsonpath_rw import parse 

print(parse('$.morefoo.morebar').find(foo)) 

そして第二:

print(parse("$.morefoo[0].morebar.bacon").find(foo)) 

PS:代替シンプルなライブラリはまた、辞書を支援することは、よりXPath風とpython-json-pointerです構文。

+0

jsonpathはevalを使い、jsonpath-rwは維持されていないように見えます(いくつかの機能が欠けていると言われますが、試していません)。 –

15

私が確認できた、さらには非常に積極的に開発された最良のライブラリの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] 
0

簡潔さはあなたの空想の場合:もちろん

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) 

、あなただけがdictsを持っている場合、それは簡単です:

def xpath(root, path, sch='/'): 
    return reduce(lambda acc, nxt: acc[nxt], 
        path.split(sch), 
        root) 

あなたのパススペックのエラーを見つけることを幸運;-)

関連する問題