python中修改局部json的思路

背景

希望修改某个路径下的字段,但是不希望覆盖其他字段。

{
  "name": "张三",
  "age": 18
}

类似于上下对比这样。

{
  "name": "张三",
  "age": 21
}

方案

get - set

使用get获取整个对象,然后修改整个对象后使用set。

回调函数

def callback(obj):
    obj['age'] = 21
    return obj

隔应的地方有两点:

  1. python的匿名函数只能有一条表达式,因此即使是上面这种简单的语句也得写一个具名函数,非常啰嗦。
  2. 事实上在执行obj['age'] = 21的时候值的修改已经完成(对象的话),但是考虑到还有字符串等简单数值,因此还是有返回值,并且内部有赋值操作。因此如果是对象的话,会有一次无效的赋值。(类似于obj=obj)

总的来说,完全没有简洁性可言。

pydash

image
比较完美地解决了问题,但是我不希望这种小问题都引入一个库。

自设计路径

仅仅是思路,因此直接使用ai代码做示例。

import json
from pathlib import Path
from datetime import datetime

def update_json_field_by_path(file_path: str, indices_and_keys: list, new_value: any) -> bool:
    """
    通过指定索引和键的路径,安全地修改 JSON 文件中的值。

    Args:
        file_path: JSON 文件的路径。
        indices_and_keys: 从根到目标字段的路径列表。
                          例如: ["hika", "latest_login"] 或 [0, "hika", "latest_login"]
        new_value: 要写入的新值。

    Returns:
        bool: 如果成功找到并更新了字段,返回 True;否则返回 False。
    """
    file = Path(file_path)

    # --- 1. 读取数据 ---
    try:
        if not file.exists():
            print(f"错误:文件不存在于路径 '{file_path}'")
            return False
        with open(file, 'r', encoding='utf-8') as f:
            data = json.load(f)
    except Exception as e:
        print(f"读取或解析 JSON 文件时发生错误: {e}")
        return False

    # --- 2. 遍历路径找到目标字段 ---
    current_level = data
    path_trace = []

    try:
        # 遍历路径列表,直到倒数第二个元素 (即目标字段的父级)
        for i, key_or_index in enumerate(indices_and_keys[:-1]):
            path_trace.append(str(key_or_index))

            # 检查当前级别是否包含下一个键/索引
            if isinstance(current_level, list):
                # 如果是列表,期望 key_or_index 是整数索引
                current_level = current_level[int(key_or_index)]
            elif isinstance(current_level, dict):
                # 如果是字典,期望 key_or_index 是字符串键
                current_level = current_level[str(key_or_index)]
            else:
                raise TypeError(f"路径错误:'{key_or_index}' 无法应用于 {type(current_level).__name__} 类型。路径终止于:{' -> '.join(path_trace)}")

        # 最终的键/字段名是路径列表的最后一个元素
        final_key = indices_and_keys[-1]

        # --- 3. 修改目标字段 ---
        if isinstance(current_level, dict) and str(final_key) in current_level:
            # 这一步是关键:只修改了内存中 data 变量的深层嵌套字段
            current_level[str(final_key)] = new_value
            print(f"字段路径 {' -> '.join(path_trace + [str(final_key)])} 已更新为: {new_value}")
        else:
            print(f"错误:无法在路径 {' -> '.join(path_trace)} 中找到字段 '{final_key}' 或目标不是字典。")
            return False

    except (IndexError, KeyError, TypeError, ValueError) as e:
        print(f"错误:无效的路径或结构不匹配。详细错误: {e}")
        return False
        
    # --- 4. 写入数据 (覆盖原文件) ---
    try:
        with open(file, 'w', encoding='utf-8') as f:
            # 使用 indent=4 保持文件格式美观
            json.dump(data, f, indent=4, ensure_ascii=False)
        return True
    except Exception as e:
        print(f"写入文件时发生错误: {e}")
        return False

# ----------------------------------------------------
# 演示使用
# ----------------------------------------------------
USER_FILE = "users.json"
# 使用当前时间作为新值
current_time = datetime.now().isoformat(timespec='seconds')

# --- 运行示例 ---
# 1. 初始化文件 (请确保文件结构与示例假设一致)
initial_data = [
    {"hika": {"latest_login": "", "email": "h.nagi@example.com"}, "id": 1},
    {"xxgal": {"latest_login": "", "email": "x.gal@example.com"}, "id": 2}
]
with open(USER_FILE, 'w', encoding='utf-8') as f:
    json.dump(initial_data, f, indent=4)

print("--- 原始文件内容 ---")
print(open(USER_FILE, 'r', encoding='utf-8').read())
print("--------------------")

# 目标:修改 data[0]['hika']['latest_login']
path_to_modify = [0, "hika", "latest_login"]
update_successful = update_json_field_by_path(USER_FILE, path_to_modify, current_time)

if update_successful:
    print("\n--- 更新后的文件内容 (验证是否只修改了目标字段) ---")
    print(open(USER_FILE, 'r', encoding='utf-8').read())
    print("-------------------------------------------------------")

结论

折腾来折腾去,还不如直接获取然后修改。看上去不简洁的方法,反而变成了最简洁的方法。

posted @ 2025-10-13 20:01  魂祈梦  阅读(6)  评论(0)    收藏  举报