Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions update_json.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
"""Utilities for updating JSON files."""
from __future__ import annotations

import json
import os
from collections.abc import Mapping
from typing import Any


def update_json_file(path: str, new_data: Mapping[str, Any]) -> bool:
"""Update JSON keys in ``path`` with values from ``new_data``.

The function reads the current JSON content from ``path``. If the file does not
exist, it is created with ``new_data`` as its content. When the file exists, only
keys already present in the JSON document are updated. Other keys in
``new_data`` are ignored so the structure of the original document is
preserved.

Args:
path: Path to the JSON file that should be updated.
new_data: Mapping containing key/value pairs to update.

Returns:
``True`` if the update succeeds, otherwise ``False``.
"""

if not isinstance(new_data, Mapping):
return False

try:
existing_data: dict[str, Any] = {}
file_exists = os.path.exists(path)

if file_exists:
with open(path, "r", encoding="utf-8") as file:
content = file.read().strip()

if content:
loaded = json.loads(content)
if not isinstance(loaded, dict):
return False
existing_data = loaded
else:
directory = os.path.dirname(path)
if directory:
os.makedirs(directory, exist_ok=True)

if file_exists:
for key, value in new_data.items():
if key in existing_data:
existing_data[key] = value
Comment on lines +48 to +51

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Overwrite nested JSON objects instead of merging

When the target file exists, the loop replaces an entire value whenever the top-level key is present. If that value is itself a dictionary, providing a partial update will clobber unspecified nested keys. For example, an existing document { "settings": { "theme": "dark", "lang": "en" } } updated with new_data={"settings": {"lang": "fr"}} results in {"settings": {"lang": "fr"}}, losing theme. This contradicts the docstring’s promise to preserve the original structure and can silently drop data. A recursive merge is needed to update only the provided nested keys.

Useful? React with 👍 / 👎.

else:
existing_data = dict(new_data)

with open(path, "w", encoding="utf-8") as file:
json.dump(existing_data, file, ensure_ascii=False, indent=4)
file.write("\n")

return True
except (OSError, json.JSONDecodeError):
return False