54

ネストされた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年編集]この質問と受け入れられた答えは古くからあります。新しい回答は、元の回答よりもうまくいく可能性があります。しかし、私はそれらをテストしなかったので、受け入れられた答えを変更しません。

4

11 に答える 11

22

私が特定できた最高のライブラリの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]
于 2014-09-26T01:34:19.743 に答える
17

これを行う簡単な方法があります。

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'}を保持します。

于 2013-05-12T13:53:52.503 に答える
15

正確には美しくはありませんが、次のようなsthを使用する場合があります

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

    return elem

もちろん、これはインデックスのようなxp​​athのものをサポートしていません... /unutbuが示したキートラップは言うまでもありません。

于 2011-09-06T13:25:53.677 に答える
13

JSONPATH構文をサポートする新しいjsonpath-rwライブラリがありますが、必要に応じてPythonディクショナリ配列用です。

したがって、最初の例は次のようになります。

from jsonpath_rw import parse

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

そして2番目:

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

PS:辞書もサポートする代替のより単純なライブラリは、よりXPathに似た構文を持つpython-json-pointerです。

于 2014-01-01T10:22:40.793 に答える
10

dict> jmespath

JSONのクエリ言語であり、Python実装を備えたJMESPathを使用できます。

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

dict> xml> xpath

もう1つのオプションは、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']

Jsonポインター

さらに別のオプションは、Python実装を持つIETF仕様であるJsonPointerです。

jsonpointer-pythonチュートリアルから:

from jsonpointer import resolve_pointer

obj = {"foo": {"anArray": [ {"prop": 44}], "another prop": {"baz": "A string" }}}

resolve_pointer(obj, '') == obj
# True

resolve_pointer(obj, '/foo/another%20prop/baz') == obj['foo']['another prop']['baz']
# True

>>> resolve_pointer(obj, '/foo/anArray/0') == obj['foo']['anArray'][0]
# True

于 2018-07-08T20:55:05.957 に答える
5

簡潔さがあなたの空想であるならば:

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)

もちろん、口述だけがある場合は、もっと簡単です。

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

パススペックのエラーを見つけて頑張ってください;-)

于 2018-02-01T17:48:11.207 に答える
2

別の選択肢(ジェリービーンズによって提案されたもの以外)はこれです:

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")
于 2011-09-06T13:30:45.387 に答える
1

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

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

処理されますか?

foo.select("///")

あいまいになります。

于 2011-09-06T13:12:54.447 に答える
1

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

このプロジェクトJSONPATHがあります。これは、人々が意図したこととは逆のことを行えるようにすることを目的としています(XPATHが与えられた場合、Pythonオブジェクトを介して簡単にアクセスできるようにする方法)。

于 2011-09-06T13:17:37.297 に答える
0
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)

dictのリストに追加することもできるSaveDict(value、var、* arg)関数があります...

于 2018-09-23T23:32:32.580 に答える
0

このリンクから参照します。

次のコードは、Pythonで実装されたjsonxpathベース解析用です。

import json
import xmltodict

# Parse the json string
class jsonprase(object):
    def __init__(self, json_value):
        try:
            self.json_value = json.loads(json_value)
        except Exception :
            raise ValueError('must be a json str value')


    def find_json_node_by_xpath(self, xpath):
        elem = self.json_value
        nodes = xpath.strip("/").split("/")
        for x in range(len(nodes)):
            try:
                elem = elem.get(nodes[x])
            except AttributeError:
                elem = [y.get(nodes[x]) for y in elem]
        return elem

    def datalength(self, xpath="/"):
        return len(self.find_json_node_by_xpath(xpath))

    @property
    def json_to_xml(self):
        try:
            root = {"root": self.json_value}
            xml = xmltodict.unparse(root, pretty=True)
        except ArithmeticError :
            pyapilog().error(e)
        return xml

Jsonのテスト:

{
    "responseHeader": {
        "zkConnected": true,
        "status": 0,
        "QTime": 2675,
        "params": {
            "q": "TxnInitTime:[2021-11-01T00:00:00Z TO 2021-11-30T23:59:59Z] AND Status:6",
            "stats": "on",
            "stats.facet": "CountryCode",
            "rows": "0",
            "wt": "json",
            "stats.field": "ItemPrice"
        }
    },
    "response": {
        "numFound": 15162439,
        "start": 0,
        "maxScore": 1.8660598,
        "docs": []
    }
}

上記の入力jsonから値を読み取るためのテストコード。

numFound = jsonprase(ABOVE_INPUT_JSON).find_json_node_by_xpath('/response/numFound')
print(numFound)
于 2021-11-09T12:02:11.307 に答える