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

Ric*_*kyA 39 python xpath jmespath json-path-expression

有没有办法为嵌套的python词典定义XPath类型查询.

像这样的东西:

foo = {
  'spam':'eggs',
  'morefoo': {
               'bar':'soap',
               'morebar': {'bacon' : 'foobar'}
              }
   }

print( foo.select("/morefoo/morebar") )

>> {'bacon' : 'foobar'}
Run Code Online (Sandbox Code Playgroud)

我还需要选择嵌套列表;)

这可以通过@ 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")
Run Code Online (Sandbox Code Playgroud)

[编辑2016]这个问题和接受的答案是古老的.较新的答案可能比原始答案更好地完成工作.但是我没有测试它们所以我不会改变接受的答案.

nik*_*lay 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]
Run Code Online (Sandbox Code Playgroud)


小智 14

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

http://github.com/akesterson/dpath-python

$ easy_install dpath
>>> dpath.util.search(YOUR_DICTIONARY, "morefoo/morebar")
Run Code Online (Sandbox Code Playgroud)

......完成了 或者,如果您不希望将结果返回到视图中(保留路径的合并字典),请改为:

$ easy_install dpath
>>> for (path, value) in dpath.util.search(YOUR_DICTIONARY, "morefoo/morebar", yielded=True)
Run Code Online (Sandbox Code Playgroud)

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


Joh*_*rra 12

不是很漂亮,但你可能会喜欢......

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

    return elem
Run Code Online (Sandbox Code Playgroud)

这当然不支持索引之类的xpath东西......更不用说/unutbu指示的关键陷阱了.

  • @nikolay这只是猜测还是有解决方案可以更好地解决这个问题? (9认同)

ank*_*tis 12

有更新的jsonpath-rw库支持JSONPATH语法,但是对于python 词典数组,正如您所希望的那样.

所以你的第一个例子变成:

from jsonpath_rw import parse

print( parse('$.morefoo.morebar').find(foo) )
Run Code Online (Sandbox Code Playgroud)

第二个:

print( parse("$.morefoo[0].morebar.bacon").find(foo) )
Run Code Online (Sandbox Code Playgroud)

PS:支持字典的另一个更简单的库是python-json-pointer,它具有更多类似XPath的语法.


ccp*_*zza 8

字典 > 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'
Run Code Online (Sandbox Code Playgroud)

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']
Run Code Online (Sandbox Code Playgroud)

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

Run Code Online (Sandbox Code Playgroud)


小智 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)
Run Code Online (Sandbox Code Playgroud)

当然,如果你只有字典,那就更简单了:

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

祝你在你的路径规范中找到任何错误 ;-)