json 查询 returns 父元素和子数据?

json query that returns parent element and child data?

鉴于以下 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 查询语言..

不确定为什么需要查询语言这很简单

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

如果值字典总是以“_status”和"md5"作为键,那就更容易了

dict((k,v["_status"]["md5"]) for k,v in json_result.items()) 

或者我认为你可以做类似的事情

t = Tree(db)
>>> dict(zip(t.execute("$."),t.execute('$..md5'))

虽然我不知道它会很合适地匹配它们...

实现新查询语言的解决方案:

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 语法,但这应该主要是繁重的工作)。

错过了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 '?'=ignore 错误的路径(如测试标量的键)。从结果路径 ($p) 过滤并用 '{}' = object 包围结果。然后那些在一个数组中([] 围绕整个表达式)然后 "added/merged" 在一起 |add

https://stedolan.github.io/jq/

如果您的 json 结构良好,即。保证你会有 _statusmd5 子元素,你可以只加载 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')]

这是完成这项工作的 JSONiq 代码:

{|
    for $key in keys($document)
    return {
        $key: $document.$key._status.md5
    }
|}

您可以使用 Zorba 引擎执行它here

如果您提到的 105 页手册是规范,我不建议作为 JSONiq 用户阅读它。我宁愿建议阅读教程或在线书籍,它们给出了更温和的介绍。

在 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