来自键路径的嵌套字典值

2025-03-25 08:47:00
admin
原创
23
摘要:问题描述:借助键路径从嵌套字典中获取值,如下所示dict:json = { "app": { "Garden": { "Flowers": { "Red flo...

问题描述:

借助键路径从嵌套字典中获取值,如下所示dict

json = {
    "app": {
        "Garden": {
            "Flowers": {
                "Red flower": "Rose",
                "White Flower": "Jasmine",
                "Yellow Flower": "Marigold"
            }
        },
        "Fruits": {
            "Yellow fruit": "Mango",
            "Green fruit": "Guava",
            "White Flower": "groovy"
        },
        "Trees": {
            "label": {
                "Yellow fruit": "Pumpkin",
                "White Flower": "Bogan"
            }
        }
    }

该方法的输入参数是用点分隔的键路径,从键路径 = “app.Garden.Flowers.white Flower” 需要打印“Jasmine”。到目前为止我的代码:

import json
with open('data.json') as data_file:    
  j = json.load(data_file)


def find(element, JSON):     
  paths = element.split(".")  
  # print JSON[paths[0]][paths[1]][paths[2]][paths[3]]
  for i in range(0,len(paths)):
    data = JSON[paths[i]]
    # data = data[paths[i+1]]
    print data



find('app.Garden.Flowers.White Flower',j)

解决方案 1:

这是fold的一个实例。你可以像这样简洁地编写它:

from functools import reduce
import operator

def find(element, json):
    return reduce(operator.getitem, element.split('.'), json)

或者更符合 Python 风格(因为reduce()可读性差而不受欢迎)如下:

def find(element, json):
    keys = element.split('.')
    rv = json
    for key in keys:
        rv = rv[key]
    return rv

j = {"app": {
    "Garden": {
        "Flowers": {
            "Red flower": "Rose",
            "White Flower": "Jasmine",
            "Yellow Flower": "Marigold"
        }
    },
    "Fruits": {
        "Yellow fruit": "Mango",
        "Green fruit": "Guava",
        "White Flower": "groovy"
    },
    "Trees": {
        "label": {
            "Yellow fruit": "Pumpkin",
            "White Flower": "Bogan"
        }
    }
}}
print find('app.Garden.Flowers.White Flower', j)

解决方案 2:

我建议您使用python-benedict,这是一个具有完整键路径支持和许多实用方法的 python dict 子类。

您只需要投射您现有的字典:

d = benedict(json)
# now your keys support dotted keypaths
print(d['app.Garden.Flower.White Flower'])

这里是库和文档:
https://github.com/fabiocaccamo/python-benedict

注:我是这个项目的作者

解决方案 3:

选项 1:Cisco 的 pyats 库 [其 ac 扩展]

  • 它快速且超级快(如果需要,可以用时间测量)

  • Javascript 式用法 [括号查找、点查找、组合查找]

  • 缺少键的点式查找会引发属性错误,括号或默认的 python 字典查找会导致 KeyError。

pip install pyats pyats-datastructures pyats-utils
from pyats.datastructures import NestedAttrDict
item = {"specifications": {"os": {"value": "Android"}}}
path = "specifications.os.value"
x = NestedAttrDict(item)
print(x[path])# prints Android
print(x['specifications'].os.value)# prints Android
print(x['specifications']['os']['value'])#prints Android
print(x['specifications'].os.value1)# raises Attribute Error

选项 2:pyats.utils chainget

  • 超级快(如果需要,可以用 timeit 测量)

from pyats.utils import utils
item = {"specifications": {"os": {"value": "Android"}}}
path = "specifications.os.value"
path1 = "specifications.os.value1"
print(utils.chainget(item,path))# prints android (string version)
print(utils.chainget(item,path.split('.')))# prints android(array version)
print(utils.chainget(item,path1))# raises KeyError

选项 3:不使用外部库的 Python

  1. 与 lambda 相比速度更快。

  2. 不需要像 lambda 和其他情况那样进行单独的错误处理。

  3. 可读且简洁,可以作为项目中的实用函数/助手

from functools import reduce
item = {"specifications": {"os": {"value": "Android"}}}
path1 = "specifications.family.value"
path2 = "specifications.family.value1"

def test1():
    print(reduce(dict.get, path1.split('.'), item))

def test2():
    print(reduce(dict.get, path2.split('.'), item))

test1() # prints Android
test2() # prints None

解决方案 4:

您的代码很大程度上依赖于键名中不出现点,您也许可以控制这一点,但不一定如此。

我将使用元素名称列表来寻求通用解决方案,然后通过拆分键名称的虚线列表来生成列表:

class ExtendedDict(dict):
    """changes a normal dict into one where you can hand a list
    as first argument to .get() and it will do a recursive lookup
    result = x.get(['a', 'b', 'c'], default_val)
    """
    def multi_level_get(self, key, default=None):
        if not isinstance(key, list):
            return self.get(key, default)
        # assume that the key is a list of recursively accessible dicts
        def get_one_level(key_list, level, d):
            if level >= len(key_list):
                if level > len(key_list):
                    raise IndexError
                return d[key_list[level-1]]
            return get_one_level(key_list, level+1, d[key_list[level-1]])

        try:
            return get_one_level(key, 1, self)
        except KeyError:
            return default

    get = multi_level_get # if you delete this, you can still use the multi_level-get

一旦你有了这个类,你就可以很容易地转换你的字典并得到“Jasmine”:

json = {
        "app": {
            "Garden": {
                "Flowers": {
                    "Red flower": "Rose",
                    "White Flower": "Jasmine",
                    "Yellow Flower": "Marigold"
                }
            },
            "Fruits": {
                "Yellow fruit": "Mango",
                "Green fruit": "Guava",
                "White Flower": "groovy"
            },
            "Trees": {
                "label": {
                    "Yellow fruit": "Pumpkin",
                    "White Flower": "Bogan"
                }
            }
        }
    }

j = ExtendedDict(json)
print j.get('app.Garden.Flowers.White Flower'.split('.'))

将会给你:

Jasmine

与普通get()的字典一样,None如果你指定的键(列表)在树中的任何地方都不存在,你就会得到结果,并且可以指定第二个参数作为返回值,而不是None

解决方案 5:

单行:

from functools import reduce

a = {"foo" : { "bar" : "blah" }}
path = "foo.bar"

reduce(lambda acc,i: acc[i], path.split('.'), a)

解决方案 6:

非常接近。您需要(如您在评论中所述)递归遍历主 JSON 对象。您可以通过存储最外层键/值的结果来实现这一点,然后使用该结果获取下一个键/值,依此类推,直到您没有路径为止。

def find(element, JSON):     
  paths = element.split(".")
  data = JSON
  for i in range(0,len(paths)):
    data = data[paths[i]]
  print data

但您仍然需要警惕 KeyErrors。

解决方案 7:

编写了与字典中的列表一起处理的函数。

d = {'test': [
    {'value1': 'val'},
    {'value1': 'val2'}]}


def find_element(keys: list, dictionary: dict):
    rv = dictionary
    if isinstance(dictionary, dict):
        rv = find_element(keys[1:], rv[keys[0]])
    elif isinstance(dictionary, list):
        if keys[0].isnumeric():
            rv = find_element(keys[1:], dictionary[int(keys[0])])
    else:
        return rv
    return rv


val = find_element('test.1.value1'.split('.'), d)

解决方案 8:

添加一种使用 jsonpath-ng 的方法,以防有人发现它更适合他们的需要:

from jsonpath_ng import parse
parse('app.Garden.Flowers."White Flower"').find(json)[0].value
'Jasmine'

有很多使用 jsonpath-ng 使用 jsonpath 语法解析 json 的例子。

解决方案 9:

数据:

data = {
    "data": {
        "author_id": "1",
        "text": "hi msg",
        "attachments": {
            "media_keys": [
                "3_16"
            ]
        },
        "id": "2",
        "edit_history_tweet_ids": [
            "2"
        ]
    },
    "includes": {
        "media": [
            {
                "media_key": "3_16",
                "height": 500,
                "type": "photo",
                "width": 500,
                "url": "https://pbs.twimg.com/media/xxxxxx.png"
            }
        ],
        "users": [
            {
                "id": "1",
                "name": "name1",
                "username": "username1"
            }
        ]
    }
}

功能:

def get_value_from_dict(dic_obj, keys: list, default):
    """
    get value from dict with key path.
    :param dic_obj: dict
    :param keys: dict key
    :param default: default value
    :return:
    """
    if not dic_obj or not keys:
        return default

    pre_obj = dic_obj
    for key in keys:
        t = type(pre_obj)
        if t is dict:
            pre_obj = pre_obj.get(key)
        elif (t is list or t is tuple) and str(key).isdigit() and len(pre_obj) > int(key):
            pre_obj = pre_obj[int(key)]
        else:
            return default
    return pre_obj

测试:

print('media_key:', get_value_from_dict(data, 'data.attachments.media_keys'.split('.'), None))
print('username:', get_value_from_dict(data, 'includes.users.0.username'.split('.'), None))
media_key: ['3_16']
username: username1
相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   2079  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1459  
  建筑行业正处于数字化转型的关键时期,建筑产品生命周期管理(PLM)系统的实施对于提升项目效率、质量和协同性至关重要。特别是在 2025 年,基于建筑信息模型(BIM)的项目进度优化工具成为众多建筑企业关注的焦点。这些工具不仅能够整合项目全生命周期的数据,还能通过精准的分析和模拟,为项目进度管理提供强大支持。BIM 与建...
plm是什么软件   0  
  PLM系统开发的重要性与现状PLM(产品生命周期管理)系统在现代企业的产品研发、生产与管理过程中扮演着至关重要的角色。它贯穿产品从概念设计到退役的整个生命周期,整合了产品数据、流程以及人员等多方面的资源,极大地提高了企业的协同效率和创新能力。通过PLM系统,企业能够实现产品信息的集中管理与共享,不同部门之间可以实时获取...
国产plm软件   0  
  PLM(产品生命周期管理)系统在企业产品研发与管理过程中扮演着至关重要的角色。随着市场竞争的加剧和技术的飞速发展,企业对PLM系统的迭代周期优化需求日益迫切。2025年敏捷认证对项目管理提出了新的要求,其中燃尽图作为一种强大的可视化工具,在PLM系统迭代周期优化中有着广泛且重要的应用。深入探讨这些应用,对于提升企业的项...
plm系统主要干什么的   0  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用