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

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..

4b9b3361

Ответ 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')]