楼主: CDA网校
57 0

[每天一个数据分析师] 5个实用的Python JSON解析与处理自定义函数 [推广有奖]

管理员

已卖:189份资源

泰斗

4%

还不是VIP/贵宾

-

威望
3
论坛币
123742 个
通用积分
11608.5217
学术水平
278 点
热心指数
286 点
信用等级
253 点
经验
230501 点
帖子
7025
精华
19
在线时间
4396 小时
注册时间
2019-9-13
最后登录
2026-2-2

初级热心勋章

楼主
CDA网校 学生认证  发表于 2026-1-29 11:49:12 |AI写论文

+2 论坛币
k人 参与回答

经管之家送您一份

应届毕业生专属福利!

求职就业群
赵安豆老师微信:zhaoandou666

经管之家联合CDA

送您一个全额奖学金名额~ !

感谢您参与论坛问题回答

经管之家送您两个论坛币!

+2 论坛币

引言

在Python中处理JSON数据往往颇具挑战。基础的json.loads()函数仅能满足简单需求。

API响应、配置文件和数据导出文件中的JSON常常杂乱无章、结构混乱。你可能需要展平嵌套对象、安全提取值以避免键错误(KeyError)、合并多个JSON文件,或在JSON与其他格式间转换。这些任务在网页爬虫、API集成和数据处理中频繁出现。本文将带你逐一掌握5个实用函数,应对JSON解析与处理的常见场景。

这些函数的完整代码可在GitHub上获取。

1. 安全提取嵌套值

JSON对象的嵌套层级往往较深,使用方括号语法访问深层嵌套值会迅速变得棘手——只要有一个键缺失,就会触发键错误(KeyError)。

以下函数支持通过点号分隔语法访问嵌套值,并为缺失键提供默认值兜底:


def get_nested_value(data, path, default=None):
    """
    通过点号分隔语法安全提取JSON中的嵌套值。

    参数:
        data: 字典或JSON对象
        path: 点号分隔的字符串,例如 "user.profile.email"
        default: 路径不存在时返回的默认值

    返回:
        路径对应的 值,若路径不存在则返回默认值
    """

    keys = path.split('.')
    current = data

    for key in keys:
        if isinstance(current, dict):
            current = current.get(key)
            if current is None:
                return default
        elif isinstance(current, list):
            try:
                index = int(key)
                current = current[index]
            except (ValueError, IndexError):
                return default
        else:
            return default

    return current

我们用复杂的嵌套结构测试该函数:


# 示例JSON数据
user_data = {
    "user": {
        "id"123,
        "profile": {
            "name""Allie",
            "email""allie@example.com",
            "settings": {
                "theme""dark",
                "notifications"True
            }
        },
        "posts": [
            {"id"1"title""First Post"},
            {"id"2"title""Second Post"}
        ]
    }
}

# 提取值
email = get_nested_value(user_data, "user.profile.email")
theme = get_nested_value(user_data, "user.profile.settings.theme")
first_post = get_nested_value(user_data, "user.posts.0.title")
missing = get_nested_value(user_data, "user.profile.age", default=25)

print(f"邮箱:{email}")
print(f"主题:{theme}")
print(f"第一篇文章:{first_post}")
print(f"年龄(默认值):{missing}")

输出结果:


邮箱:allie@example.com
主题:dark
第一篇文章:First Post
年龄(默认值):25

该函数按点号拆分路径字符串,逐层遍历数据结构。在每一层,它会检查当前值是否为字典或列表:若为字典,使用.get(key)方法(缺失键时返回None而非触发错误);若为列表,尝试将键转换为整数索引。

默认参数为路径不存在时提供兜底,可避免代码在处理API返回的不完整或不一致JSON数据时崩溃。

此模式在处理API响应时尤为实用——这类场景中部分字段常为可选,或仅在特定条件下存在。

2. 将嵌套JSON展平为单层级字典

机器学习模型、CSV导出和数据库插入操作通常需要扁平数据结构,但API响应和配置文件多采用嵌套JSON。将嵌套对象转换为扁平键值对是常见需求。

以下函数可将嵌套JSON展平,并支持自定义分隔符:


def flatten_json(data, parent_key='', separator='_'):
    """
    将嵌套JSON展平为单层级字典。

    参数:
        data: 嵌套字典或JSON对象
        parent_key: 键的前缀(用于递归)
        separator: 连接嵌套键的字符串

    返回:
        键名拼接后的扁平字典
    """

    items = []

    if isinstance(data, dict):
        for key, value in data.items():
            new_key = f"{parent_key}{separator}{key}" if parent_key else key

            if isinstance(value, dict):
                # 递归展平嵌套字典
                items.extend(flatten_json(value, new_key, separator).items())
            elif isinstance(value, list):
                # 展平列表并添加索引键
                for i, item in enumerate(value):
                    list_key = f"{new_key}{separator}{i}"
                    if isinstance(item, (dict, list)):
                        items.extend(flatten_json(item, list_key, separator).items())
                    else:
                        items.append((list_key, item))
            else:
                items.append((new_key, value))
    else:
        items.append((parent_key, data))

    return dict(items)

我们用复杂嵌套结构测试展平功能:


# 复杂嵌套JSON
product_data = {
    "product": {
        "id"456,
        "name""笔记本电脑",
        "specs": {
            "cpu""英特尔i7",
            "ram""16GB",
            "storage": {
                "type""固态硬盘(SSD)",
                "capacity""512GB"
            }
        },
        "reviews": [
            {"rating"5"comment""表现极佳"},
            {"rating"4"comment""性价比高"}
        ]
    }
}

flattened = flatten_json(product_data)

for key, value in flattened.items():
    print(f"{key}{value}")

输出结果:


product_id: 456
product_name: 笔记本电脑
product_specs_cpu: 英特尔i7
product_specs_ram: 16GB
product_specs_storage_type: 固态硬盘(SSD)
product_specs_storage_capacity: 512GB
product_reviews_0_rating: 5
product_reviews_0_comment: 表现极佳
product_reviews_1_rating: 4
product_reviews_1_comment: 性价比高

该函数通过递归处理任意深度的嵌套。遇到字典时,遍历每个键值对,将父键与当前键用分隔符拼接生成新键;遇到列表时,将索引作为键的一部分,保留数组元素的顺序和结构(例如reviews_0_rating表示第一篇评论的评分)。

分隔符参数支持自定义输出格式:根据需求可使用点号(对应点号语法)、下划线(对应蛇形命名法)或斜杠(对应路径式键名)。

此函数在将JSON API响应转换为数据框(Datafr ame)或CSV行时特别实用——这类场景要求每列拥有唯一名称。

3. 深度合并多个JSON对象

配置管理常需合并多个JSON文件,包括默认设置、环境专属配置、用户偏好等。简单的dict.update()仅能处理顶层键值,需通过深度合并递归组合嵌套结构。

以下函数实现JSON对象的深度合并:


def deep_merge_json(base, override):
    """
    深度合并两个JSON对象,覆盖字典(override)优先级更高。

    参数:
        base: 基础字典
        override: 用于覆盖/新增值的字典

    返回:
        合并后的新字典
    """

    result = base.copy()

    for key, value in override.items():
        if key in result and isinstance(result[key], dict) and isinstance(value, dict):
            # 递归合并嵌套字典
            result[key] = deep_merge_json(result[key], value)
        else:
            # 覆盖或新增值
            result[key] = value

    return result

我们测试配置文件的合并场景:


import json

# 默认配置
default_config = {
    "database": {
        "host""localhost",
        "port"5432,
        "timeout"30,
        "pool": {
            "min"2,
            "max"10
        }
    },
    "cache": {
        "enabled"True,
        "ttl"300
    },
    "logging": {
        "level""INFO"
    }
}

# 生产环境覆盖配置
prod_config = {
    "database": {
        "host""prod-db.example.com",
        "pool": {
            "min"5,
            "max"50
        }
    },
    "cache": {
        "ttl"600
    },
    "monitoring": {
        "enabled"True
    }
}

merged = deep_merge_json(default_config, prod_config)

print(json.dumps(merged, indent=2, ensure_ascii=False))

输出结果:


{
  "database": {
    "host""prod-db.example.com",
    "port"5432,
    "timeout"30,
    "pool": {
      "min"5,
      "max"50
    }
  },
  "cache": {
    "enabled"true,
    "ttl"600
  },
  "logging": {
    "level""INFO"
  },
  "monitoring": {
    "enabled"true
  }
}

该函数递归合并嵌套字典:当基础字典和覆盖字典在同一键下均为字典时,会合并这两个子字典而非直接覆盖,从而保留未被显式覆盖的值。

从结果可见,database.port和database.timeout保留默认配置,而database.host被覆盖;嵌套的连接池(pool)配置完全合并,min和max均更新为生产环境值。同时,函数会新增基础字典中不存在的键(如生产环境配置中的monitoring模块)。

可链式调用多次合并以实现多层级配置叠加:


final_config = deep_merge_json(
    deep_merge_json(default_config, prod_config),
    user_preferences  # 用户偏好配置
)

此模式在应用配置中极为常见,适用于默认配置、环境专属配置和运行时覆盖配置的多层级组合场景。

4. 按Schema或白名单过滤JSON

API返回的数据往往超出实际需求,庞大的JSON响应会降低代码可读性。有时你仅需特定字段,或在日志记录前移除敏感数据。

以下函数可按指定规则过滤JSON,仅保留所需字段:


def filter_json(data, schema):
    """
    按Schema规则过滤JSON,仅保留指定字段。

    参数:
        data: 待过滤的字典或JSON对象
        schema: 定义保留字段的字典
                用True表示保留该字段,用嵌套字典表示嵌套过滤规则

    返回:
        仅包含指定字段的过滤后字典
    """

    if not isinstance(data, dict) or not isinstance(schema, dict):
        return data

    result = {}

    for key, value in schema.items():
        if key not in data:
            continue

        if value is True:
            # 保留该字段原值
            result[key] = data[key]
        elif isinstance(value, dict):
            # 递归过滤嵌套对象
            if isinstance(data[key], dict):
                filtered_nested = filter_json(data[key], value)
                if filtered_nested:
                    result[key] = filtered_nested
            elif isinstance(data[key], list):
                # 过滤列表中的每个元素
                filtered_list = []
                for item in data[key]:
                    if isinstance(item, dict):
                        filtered_item = filter_json(item, value)
                        if filtered_item:
                            filtered_list.append(filtered_item)
                    else:
                        filtered_list.append(item)
                if filtered_list:
                    result[key] = filtered_list

    return result

我们用示例API响应测试过滤功能:


import json
# 示例API响应
api_response = {
    "user": {
        "id"789,
        "username""Cayla",
        "email""cayla@example.com",
        "password_hash""secret123",  # 敏感字段
        "profile": {
            "name""Cayla Smith",
            "bio""软件开发工程师",
            "avatar_url""https://example.com/avatar.jpg",
            "private_notes""内部备注"  # 敏感字段
        },
        "posts": [
            {
                "id"1,
                "title""Hello World",
                "content""我的第一篇文章",
                "views"100,
                "internal_score"0.85  # 内部字段
            },
            {
                "id"2,
                "title""Python技巧",
                "content""一些实用技巧",
                "views"250,
                "internal_score"0.92  # 内部字段
            }
        ]
    },
    "me tadata": {
        "request_id""abc123",
        "server""web-01"
    }
}

# 定义保留字段的Schema
public_schema = {
    "user": {
        "id"True,
        "username"True,
        "profile": {
            "name"True,
            "avatar_url"True
        },
        "posts": {
            "id"True,
            "title"True,
            "views"True
        }
    }
}

filtered = filter_json(api_response, public_schema)

print(json.dumps(filtered, indent=2, ensure_ascii=False))

输出结果:


{
  "user": {
    "id"789,
    "username""Cayla",
    "profile": {
      "name""Cayla Smith",
      "avatar_url""https://example.com/avatar.jpg"
    },
    "posts": [
      {
        "id"1,
        "title""Hello World",
        "views"100
      },
      {
        "id"2,
        "title""Python技巧",
        "views"250
      }
    ]
  }
}

Schema本质是字段白名单:将字段设为True表示保留该字段,使用嵌套字典可定义嵌套对象的过滤规则,函数会递归将规则应用于嵌套结构。

对于列表,Schema规则会应用于每个元素。示例中posts列表被过滤后,仅保留id、title和views字段,content和internal_score等内部字段被移除。

注意,password_hash和private_notes等敏感字段未出现在输出中。该函数适用于日志记录前的数据脱敏,或向前端应用传输数据时的字段筛选。

可根据不同场景创建多个Schema:例如列表页用的精简Schema、详情页用的完整Schema,以及管理员专用的全量Schema。

5. JSON与点号语法的双向转换

部分系统使用扁平键值存储,但代码中需操作嵌套JSON。在扁平点号语法键与嵌套结构间转换可解决这一矛盾。

以下两组函数实现双向转换:

将JSON转换为点号语法


def json_to_dot_notation(data, parent_key=''):
    """
    将嵌套JSON转换为扁平点号语法字典。

    参数:
        data: 嵌套字典
        parent_key: 键的前缀(用于递归)

    返回:
        点号语法键的扁平字典
    """

    items = {}

    if isinstance(data, dict):
        for key, value in data.items():
            new_key = f"{parent_key}.{key}" if parent_key else key

            if isinstance(value, dict):
                items.update(json_to_dot_notation(value, new_key))
            else:
                items[new_key] = value
    else:
        items[parent_key] = data

    return items

将点号语法转换为JSON


def dot_notation_to_json(flat_data):
    """
    将扁平点号语法字典转换为嵌套JSON。

    参数:
        flat_data: 点号语法键的扁平字典

    返回:
        嵌套字典
    """

    result = {}

    for key, value in flat_data.items():
        parts = key.split('.')
        current = result

        for i, part in enumerate(parts[:-1]):
            if part not in current:
                current[part] = {}
            current = current[part]

        current[parts[-1]] = value

    return result

我们测试完整的往返转换:


import json
# 原始嵌套JSON
config = {
    "app": {
        "name""MyApp",
        "version""1.0.0"
    },
    "database": {
        "host""localhost",
        "credentials": {
            "username""admin",
            "password""secret"
        }
    },
    "features": {
        "analytics"True,
        "notifications"False
    }
}

# 转换为点号语法(适用于环境变量等场景)
flat = json_to_dot_notation(config)
print("扁平格式:")
for key, value in flat.items():
    print(f"  {key} = {value}")

print("\n" + "="*50 + "\n")

# 转换回嵌套JSON
nested = dot_notation_to_json(flat)

print("嵌套格式:")
print(json.dumps(nested, indent=2, ensure_ascii=False))

输出结果:


扁平格式:
  app.name = MyApp
  app.version = 1.0.0
  database.host = localhost
  database.credentials.username = admin
  database.credentials.password = secret
  features.analytics = True
  features.notifications = False

==================================================

嵌套格式:
{
  "app": {
    "name": "MyApp",
    "version": "1.0.0"
  },
  "database": {
    "host": "localhost",
    "credentials": {
      "username": "admin",
      "password": "secret"
    }
  },
  "features": {
    "analytics": true,
    "notifications": false
  }
}

json_to_dot_notation函数通过递归遍历嵌套字典,用点号连接键名生成扁平结构。与前文展平函数不同,该函数不处理列表,专为纯键值结构的配置数据优化。

dot_notation_to_json函数则反向操作:按点号拆分键名,通过循环创建中间嵌套层级(处理除最后一段外的所有键片段),最终将值赋给最内层键。

这种双向转换可在扁平键值存储的约束下,保持配置数据的可读性和可维护性。

总结

JSON处理远不止基础的json.loads()函数。多数项目中,你需要工具来导航嵌套结构、转换数据形态、合并配置、过滤字段及实现格式转换。

本文中的技术同样适用于其他数据处理场景,可修改这些模式以适配xm l、YAML或自定义数据格式。

建议先集成安全提取函数以避免代码中的键错误,再根据实际需求添加其他函数。祝你编码愉快!

推荐学习书籍 《CDA一级教材》适合CDA一级考生备考,也适合业务及数据分析岗位的从业者提升自我。完整电子版已上线CDA网校,累计已有10万+在读~ !

免费加入阅读:https://edu.cda.cn/goods/show/3151?targetId=5147&preview=0

二维码

扫码加我 拉你入群

请注明:姓名-公司-职位

以便审核进群资格,未注明则拒绝

关键词:python 自定义函数 json 自定义 son

您需要登录后才可以回帖 登录 | 我要注册

本版微信群
加好友,备注cda
拉您进交流群
GMT+8, 2026-2-3 01:59