Xpath就像查询嵌套的python词典一样

时间:2011-09-06 13:01:45

标签: python xpath jmespath json-path-expression

有没有办法为嵌套的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]这个问题和接受的答案是古老的。较新的答案可能比原始答案更好地完成工作。但是我没有测试它们所以我不会改变接受的答案。

10 个答案:

答案 0 :(得分:19)

我能够识别的最好的库之一,此外,它是非常积极开发的,是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]

答案 1 :(得分:12)

现在有一种更简单的方法可以做到这一点。

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)

......完成了。在这种情况下,'价值'将保持{'培根':'foobar'}。

答案 2 :(得分:12)

有更新的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的语法。

答案 3 :(得分:10)

不完全漂亮,但你可能会像......一样使用

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

    return elem

这不支持像路径这样的xpath内容,当然......更不用说/关键陷阱unutbu指示。

答案 4 :(得分:3)

如果简洁是你的幻想:

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)

祝您在路径规格中发现任何错误; - )

答案 5 :(得分:1)

更多的工作必须放在类似XPath的选择器如何工作。 '/'是一个有效的字典键,所以如何

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

被处理?

foo.select("///")

会含糊不清。

答案 6 :(得分:1)

你是否有任何理由像XPath模式那样查询它?正如您的问题的评论者所建议的那样,它只是一个字典,因此您可以以嵌套的方式访问元素。此外,考虑到数据采用JSON格式,您可以使用simplejson模块加载它并访问元素。

有一个项目JSONPATH,它试图帮助人们做你想要做的事情(给定一个XPATH,如何通过python对象轻松访问它),这似乎更有用。

答案 7 :(得分:1)

另一个替代方案(除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")

答案 8 :(得分:0)

dict> json> jmespath

您可以使用JMESPath,它是JSON的查询语言,并且具有python implementation

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

另一种选择是使用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']

答案 9 :(得分: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)

具有SaveDict(value,var,* arg)函数,该函数甚至可以追加到字典中的列表...