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

[EDIT 2016] 这个问题和公认的答案是古老的。较新的答案可能比原始答案做得更好。但是我没有测试它们,所以我不会更改接受的答案。

4

11 回答 11

22

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

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

    return elem

当然,这不支持诸如索引之类的 xpath 内容……更不用说/unutbu 指示的关键陷阱了。

于 2011-09-06T13:25:53.677 回答
13

如您所愿,有更新的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。

于 2014-01-01T10:22:40.793 回答
10

字典 > jmespath

您可以使用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

字典 > 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']

json指针

另一个选择是Json Pointer,它是一个具有 python 实现的IETF 规范:

来自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

另一种选择(除了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")
于 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)

有一个 SaveDict(value, var, *arg) 函数,它甚至可以附加到字典中的列表...

于 2018-09-23T23:32:32.580 回答
0

我参考了这个链接。.

以下代码用于在 python 中实现的 json xpath 基本解析:

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 回答