如何递归地在嵌套的JSON中查找特定键?

35

我正在尝试从一个json文件中提取嵌套的值。我想打印出每个“id”键的值。我认为我已经接近成功,但无法弄清为什么对象类型从字典变成了列表,然后为什么我无法解析该列表。 这里是我正在使用的json链接:http://hastebin.com/ratevimixa.tex

以下是我目前的代码:

#!/usr/bin/env python
#-*- coding: utf-8 -*-

import json

json_data = open('JubJubProductions.json', 'r+')
jdata = json.loads(json_data.read().decode("utf-8"))

def recursion(dict):

    for key, value in dict.items():

        if type(value) == type(dict):
            if key != "paging":
                for key, value in value.items():
                    if isinstance (value,list):
                        print key
                        # place where I need to enter list comprehension?
                if type(value) == type(dict):
                    if key == "id":
                        print " id found " + value
                    if key != "id":
                        print key + " 1st level"
                if key == "id":
                    print key
        else:
            if key == "id":
                print "id found " + value       
if __name__ == '__main__':
    recursion(jdata)

-------------------------------------------------------------------------------------------更新

现在我处理的是它,并将返回单个id值,但不是所有id值:

#!/usr/bin/env python
#-*- coding: utf-8 -*-

import json

json_data = open('jubjubProductions', 'r+')
jdata = json.loads(json_data.read().decode("utf-8"))

def id_generator(d):
    for k, v in d.items():
        if k == "id":
            yield v
        elif isinstance(v, dict):
            for id_val in id_generator(v):
                yield id_val

if __name__ == '__main__':
    for _ in id_generator(jdata):
        print (_)

当我使用这个时,我会得到一个错误“Too many values to unpack”。不过我没有使用d.items(),因为否则我会得到“AttributeError: 'unicode' object has no attribute 'items'”。 - Farhad
6个回答

56
JSON 可能包含一个对象列表,需要进行搜索:
Python 2.7 版本:
def item_generator(json_input, lookup_key):
    if isinstance(json_input, dict):
        for k, v in json_input.iteritems():
            if k == lookup_key:
                yield v
            else:
                for child_val in item_generator(v, lookup_key):
                    yield child_val
    elif isinstance(json_input, list):
        for item in json_input:
            for item_val in item_generator(item, lookup_key):
                yield item_val

Python 3.x 版本:

def item_generator(json_input, lookup_key):
    if isinstance(json_input, dict):
        for k, v in json_input.items():
            if k == lookup_key:
                yield v
            else:
                yield from item_generator(v, lookup_key)
    elif isinstance(json_input, list):
        for item in json_input:
            yield from item_generator(item, lookup_key)

19
def id_generator(dict_var):
    for k, v in dict_var.items():
        if k == "id":
            yield v
        elif isinstance(v, dict):
            for id_val in id_generator(v):
                yield id_val

这将创建一个迭代器,它将在键“id”下的任何级别上产生每个值。示例用法(打印所有这些值):

for _ in id_generator(some_json_dict):
    print(_)

3
Bo Sunesen的回答似乎更恰当,因为涉及到对象列表。 - franchb
如果 id_val 也是一个字典,下一个也是同样的情况怎么办?在这些情况下应该怎么做? - Mujtaba

7
一些更简洁的代码(使用Python 3.x)。
def parse_json_recursively(json_object, target_key):
    if type(json_object) is dict and json_object:
        for key in json_object:
            if key == target_key:
                print("{}: {}".format(target_key, json_object[key]))
            parse_json_recursively(json_object[key], target_key)

    elif type(json_object) is list and json_object:
        for item in json_object:
            parse_json_recursively(item, target_key)


json_object = {"key1": "val1", "key2": [{"key3":"val3", "key4": "val4"}, 123, "abc"]}
target_key = "key3"
parse_json_recursively(json_object, target_key) # Ouput key3: val3


很好...我卡住了.....如果我想要一个方法/函数的返回值,而不仅仅是打印,该怎么办? - Ram Ghadiyaram

3

这是一个简单的递归函数,用于收集给定键的json文档中的所有值。 值也可以是json文档。 相应的值附加到search_result。

def json_full_search(lookup_key, json_dict, search_result = []):
    if type(json_dict) == dict:
        for key, value in  json_dict.items():
            if key == lookup_key:
                search_result.append(value)
            json_full_search(lookup_key, value, search_result)
    elif type(json_dict) == list:
        for element in json_dict:
            json_full_search(lookup_key, element, search_result)
    return search_result

Python的一般性批评。不要将[]作为默认值,因为这会导致重复调用时出现奇怪的效果。 - pauljohn32
也许我没有经历过,但当使用默认[]时产生奇怪的效果时,必须在语言中进行修复。 - Andrushenko Alexander

1

扩展到Python 3.x答案: 如果嵌套的JSON在不同的列表或字典下具有相似的键,并且您想要获取其第一个值... 以下是通用方法:

 def get_value_from_generator(json_input, lookup_key):
        value = list(item_generator(json_input, lookup_key))
        val = value[0] if value else None
        print(f'lookup_key -> value : {val}')
        return val
def item_generator(json_input, lookup_key):
    if isinstance(json_input, dict):
        for k, v in json_input.items():
            print(f'{k} -- {v}')
            if k == lookup_key:
                yield v
            else:
                yield from item_generator(v, lookup_key)
    elif isinstance(json_input, list):
        for item in json_input:
            yield from item_generator(item, lookup_key)

0
def get_val(j, s, v=None):
for k in j:
    if v == None and k == s:
        return j[k]
    elif v != None and k == s and v == j[k]:
        return True
    elif v != None and k == s and v != j[k]:
        return False
    elif isinstance(j[k], dict):
        return get_val(j[k], s, v)

你可以将下面的 JSON 列表 l 与 with 一起使用,
for l in j:
    r = get_val(l, 'key')
    print(r)

for l in j:
    r = get_val(l, 'mac', '00-60-2F-5A-04-51')
    print(r)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接