Подтвердить что ты не робот

Xpath как запрос для вложенных словарей python

Есть ли способ определить запрос типа XPath для вложенных словарей python.

Что-то вроде этого:

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] Этот вопрос и принятый ответ древние. Новые ответы могут сделать работу лучше, чем исходный ответ. Однако я не тестировал их, поэтому я не буду изменять принятый ответ.

4b9b3361

Ответ 1

Не совсем красиво, но вы можете использовать sth как

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

    return elem

Это не поддерживает такие вещи, как индексы, например, индексы,... не говоря уже о том, что отмечена клавиша / unutbu.

Ответ 2

Одна из лучших библиотек, которые я смог идентифицировать, которая, кроме того, очень активно развивается, - это извлеченный проект из 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]

Ответ 3

Теперь есть более простой способ сделать это.

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'} в этом случае.

Ответ 4

Существует новая библиотека 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.

Ответ 5

Должна быть сделана работа над тем, как будет работать селектор XPath. '/' является допустимым ключом словаря, поэтому как

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

обрабатывается?

foo.select("///")

будет неоднозначным.

Ответ 6

Есть ли у вас какая-либо причина для запроса, как у XPath-шаблона? Как сказал комментатор на ваш вопрос, это просто словарь, поэтому вы можете получить доступ к элементам в виде гнезда. Кроме того, учитывая, что данные находятся в форме JSON, вы можете использовать модуль simplejson для его загрузки и доступа к элементам.

Этот проект JSONPATH, который пытается помочь людям сделать что-то противоположное тому, что вы намереваетесь сделать (учитывая XPATH, как чтобы сделать его легко доступным через объекты python), что кажется более полезным.

Ответ 7

Другая альтернатива (помимо предложенной 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")