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
如果您的 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')]
这是完成这项工作的 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。
鉴于以下 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
如果您的 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')]
这是完成这项工作的 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。