返回父元素和子数据的json查询?

时间:2015-09-12 00:11:53

标签: python json jsoniq

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

6 个答案:

答案 0 :(得分:6)

不确定为什么要使用查询语言这很容易

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

虽然我不知道它会匹配得恰到好处......

答案 1 :(得分:4)

以下是完成工作的JSONiq代码:

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

您可以使用Zorba引擎执行here

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

答案 2 :(得分:3)

在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。我正在使用OP的密钥()来展示如何在文档的根目录中创建适用于文档中任何位置的完整查询。

PS2。我可能会添加新函数,使其看起来像:object([keys($。*),$ .. md5])。如果你愿意的话,可以给我发推文http://twitter.com/adriankal

答案 3 :(得分:2)

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

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语法,但这应该是大部分工作)。

答案 4 :(得分:2)

错过了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/

答案 5 :(得分:1)

如果你的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')]