resueltos recorrer lista elementos ejercicios diccionarios diccionario dentro convertir agregar python xpath jmespath json-path-expression

recorrer - Xpath como consulta para diccionarios anidados python



lista de diccionarios python (9)

dict> json> jmespath

Puede usar JMESPath que es un lenguaje de consulta para JSON, y que tiene una implementación de python .

import jmespath # pip install jmespath data = {''root'': {''section'': {''item1'': ''value1'', ''item2'': ''value2''}}} jmespath.search(''root.section.item2'', data) Out[42]: ''value2''

La sintaxis de la consulta jmespath y ejemplos en vivo: http://jmespath.org/tutorial.html

dict> xml> xpath

Otra opción sería convertir sus diccionarios a XML usando algo como dicttoxml y luego usar expresiones regulares de XPath, por ejemplo, a través de lxml o cualquier otra biblioteca que prefiera.

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

¿Hay alguna forma de definir una consulta de tipo XPath para los diccionarios de Python anidados?

Algo como esto:

foo = { ''spam'':''eggs'', ''morefoo'': { ''bar'':''soap'', ''morebar'': {''bacon'' : ''foobar''} } } print( foo.select("/morefoo/morebar") ) >> {''bacon'' : ''foobar''}

También necesitaba seleccionar listas anidadas;)

Esto se puede hacer fácilmente con la solución de @ 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] Esta pregunta y la respuesta aceptada son antiguas. Las respuestas más nuevas pueden hacer el trabajo mejor que la respuesta original. Sin embargo, no los probé, así que no cambiaré la respuesta aceptada.


¿Hay alguna razón para que lo consultes de la misma manera que el patrón XPath? Como lo sugirió el comentarista de su pregunta, es solo un diccionario, por lo que puede acceder a los elementos de una manera nida. Además, considerando que los datos tienen la forma de JSON, puede usar el módulo simplejson para cargarlo y acceder a los elementos también.

Existe este proyecto JSONPATH , que trata de ayudar a las personas a hacer lo contrario de lo que pretendes hacer (dado un XPATH, cómo hacer que sea fácilmente accesible a través de objetos de pitón), lo que parece más útil.


Existe la biblioteca jsonpath-rw más jsonpath-rw admite una sintaxis JSONPATH pero para los diccionarios y arreglos de Python, como lo desea.

Entonces su primer ejemplo se convierte en:

from jsonpath_rw import parse print( parse(''$.morefoo.morebar'').find(foo) )

Y el 2do:

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

PD: una biblioteca alternativa más simple que también admite diccionarios es python-json-pointer con una sintaxis similar a XPath.


Hay una manera más fácil de hacer esto ahora.

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

$ easy_install dpath >>> dpath.util.search(YOUR_DICTIONARY, "morefoo/morebar")

... hecho. O bien, si no desea obtener los resultados en una vista (diccionario fusionado que conserva las rutas), en su lugar, réselos:

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

... y hecho. ''value'' mantendrá {''bacon'': ''foobar''} en ese caso.


No es exactamente hermoso, pero puede usar algo así como

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

Esto no es compatible con xpath, como los índices, por supuesto ... por no mencionar la clave / trampa unutbu indicada.


Otra alternativa (además de la sugerida por jellybean ) es esta:

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


Se debería poner más trabajo en cómo funcionaría el selector tipo XPath. ''/'' es una clave de diccionario válida, ¿cómo lo haría?

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

¿ser manejado?

foo.select("///")

sería ambiguo


Si la ternura es su fantasía:

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)

Por supuesto, si solo tienes dicts, entonces es más simple:

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

Buena suerte para encontrar cualquier error en tu camino, aunque ;-)


Una de las mejores bibliotecas que he podido identificar, que, además, se desarrolla muy activamente, es un proyecto extraído de boto: JMESPath . Tiene una sintaxis muy poderosa de hacer cosas que normalmente tomarían páginas de código para expresar.

Aquí hay unos ejemplos:

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]