Json query, который возвращает родительский элемент и дочерние данные?
Учитывая следующий json:
{
"README.rst": {
"_status": {
"md5": "952ee56fa6ce36c752117e79cc381df8"
}
},
"docs/conf.py": {
"_status": {
"md5": "6e9c7d805a1d33f0719b14fe28554ab1"
}
}
}
есть ли язык запроса, который может произвести:
{
"README.rst": "952ee56fa6ce36c752117e79cc381df8",
"docs/conf.py": "6e9c7d805a1d33f0719b14fe28554ab1",
}
Моя лучшая попытка до сих пор с JMESPath (http://jmespath.org/) не очень близка:
>>> jmespath.search('*.*.md5[]', db)
['952ee56fa6ce36c752117e79cc381df8', '6e9c7d805a1d33f0719b14fe28554ab1']
Я попал в ту же точку с ObjectPath (http://objectpath.org):
>>> t = Tree(db)
>>> list(t.execute('$..md5'))
['952ee56fa6ce36c752117e79cc381df8', '6e9c7d805a1d33f0719b14fe28554ab1']
Я не мог понять JSONiq (действительно ли мне нужно прочитать руководство на странице 105 для этого?) Это мой первый взгляд на языки запросов json..
Ответы
Ответ 1
Пропущено требование python, но если вы хотите вызвать внешнюю программу, это все равно будет работать.
Обратите внимание, что для этого требуется jq >= 1.5.
# If single "key" $p[0] has multiple md5 keys, this will reduce the array to one key.
cat /tmp/test.json | \
jq-1.5 '[paths(has("md5")?) as $p | { ($p[0]): getpath($p)["md5"]}] | add '
# this will not create single object, but you'll see all key, md5 combinations
cat /tmp/test.json | \
jq-1.5 '[paths(has("md5")?) as $p | { ($p[0]): getpath($p)["md5"]}] '
Получить пути с помощью "md5" -key '?' = игнорировать ошибки (например, сканирование сканера для ключа). Из результирующих путей ($ p) и результата окружающего звучания с помощью объекта '{}' =. И тогда они находятся в массиве ([], окружающем все выражение), который затем "добавляется/объединяется" вместе |add
https://stedolan.github.io/jq/
Ответ 2
не уверен, почему вы хотите, чтобы язык запросов был довольно легким.
def find_key(data,key="md5"):
for k,v in data.items():
if k== key: return v
if isinstance(v,dict):
result = find_key(v,key)
if result:return result
dict((k,find_key(v,"md5")) for k,v in json_result.items())
это еще проще, если значение dict всегда имеет "_status" и "md5" как клавиши
dict((k,v["_status"]["md5"]) for k,v in json_result.items())
В качестве альтернативы, я думаю, вы могли бы сделать что-то вроде
t = Tree(db)
>>> dict(zip(t.execute("$."),t.execute('$..md5'))
хотя я не знаю, что это будет соответствовать им совершенно правильно...
Ответ 3
Вот код JSONiq, который выполняет задание:
{|
for $key in keys($document)
return {
$key: $document.$key._status.md5
}
|}
Вы можете выполнить здесь с движком Zorba.
Если приведенное выше 105-страничное руководство является спецификацией, я не рекомендую читать его как пользователь JSONiq. Я бы скорее посоветовал читать учебники или книги в Интернете, которые дают более мягкое введение.
Ответ 4
Решение, которое реализует новый язык запросов:
def keylist(db):
"Return all the keys in db."
def _keylist(db, prefix, res):
if prefix is None:
prefix = []
for key, val in db.items():
if isinstance(val, dict):
_keylist(val, prefix + [key], res)
else:
res.append(prefix + [key])
res = []
_keylist(db, [], res)
return ['::'.join(key) for key in res]
def get_key(db, key):
"Get path and value from key."
def _get_key(db, key, path):
k = key[0]
if len(key) == 1:
return path + [k, db[k]]
return _get_key(db[k], key[1:], path + [k])
return _get_key(db, key, [])
def search(query, db):
"Convert query to regex and use it to search key space."
keys = keylist(db)
query = query.replace('*', r'(?:.*?)')
matching = [key for key in keys if re.match(query, key)]
res = [get_key(db, key.split('::')) for key in matching]
return dict(('::'.join(r[:-1]), r[-1]) for r in res)
который дает мне то, что довольно близко к требованиям:
>>> pprint.pprint(search("*::md5", db))
{'README.rst::_status::md5': '952ee56fa6ce36c752117e79cc381df8',
'docs/conf.py::_status::md5': '6e9c7d805a1d33f0719b14fe28554ab1'}
и язык запросов, который выглядит как гибрид glob/re (если мы создаем новый язык, по крайней мере, сделаем его знакомым):
>>> pprint.pprint(search("docs*::md5", db))
{'docs/conf.py::_status::md5': '6e9c7d805a1d33f0719b14fe28554ab1'}
поскольку данные содержат пути к файлам, которые я случайно использовал ::
как разделитель путей. (Я уверен, что он еще не обрабатывает полную грамматику json, но это должно быть в основном ворчанием).
Ответ 5
Сделайте в ObjectPath:
l = op.execute("[keys($.*), $..md5]")
вы получите:
[
[
"README.rst",
"docs/conf.py"
],
[
"952ee56fa6ce36c752117e79cc381df8",
"6e9c7d805a1d33f0719b14fe28554ab1"
]
]
затем в Python:
dict(zip(l[0],l[1]))
чтобы получить:
{
'README.rst': '952ee56fa6ce36c752117e79cc381df8',
'docs/conf.py': '6e9c7d805a1d33f0719b14fe28554ab1'
}
Надеюсь, что это поможет.:)
PS. Я использую OPs 'keys(), чтобы показать, как сделать полный запрос, который работает в любом месте документа не только, когда ключи находятся в корневом каталоге документа.
PS2. Я мог бы добавить новую функцию, чтобы она выглядела так: object ([keys ($. *), $.. md5]). Стреляйте меня в твит < http://twitter.com/adriankal, если вы этого хотите.
Ответ 6
Если ваш json хорошо структурирован, т.е. убедитесь, что у вас есть подтексты _status
и md5
, вы можете просто загрузить json и использовать понимание списка, чтобы выплюнуть предметы, которые вы ищете.
>>> import json
>>> my_json = json.loads(json_string)
>>> print [(key, value['_status']['md5']) for key, value in my_json.iteritems()]
[(u'README.rst', u'952ee56fa6ce36c752117e79cc381df8'), (u'docs/conf.py', u'6e9c7d805a1d33f0719b14fe28554ab1')]