Easy Json
py_simple.easy_json
easy_json is built to simplify working with json files
EasyJsonError
Bases: Exception
Raised when a JSON file can't be opened or parsed.
Wraps the underlying error (missing file, bad permissions, invalid JSON syntax, etc.) so py_simple functions can fail with one consistent, easy-to-read exception instead of a random builtin one.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
message
|
str
|
Human-readable description of what went wrong. |
required |
compare_json(data1=None, data2=None, filepath1=None, filepath2=None, path='')
Compares two JSON objects or files and returns a dictionary describing the differences: added keys, removed keys, and changed values. Supports comparison of both dictionaries and lists. Nested structures are traversed recursively.
Provide exactly one of data arguments or filepath arguments
— not both for each side.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data1
|
dict | list
|
First JSON object to compare. |
None
|
data2
|
dict | list
|
Second JSON object to compare. |
None
|
filepath1
|
str
|
Path to the first JSON file. |
None
|
filepath2
|
str
|
Path to the second JSON file. |
None
|
path
|
str
|
Internal use only — the current dot-notation path during recursion. |
''
|
Returns:
| Name | Type | Description |
|---|---|---|
dict |
dict
|
A dictionary with three keys:
- |
Raises:
| Type | Description |
|---|---|
EasyJsonError
|
If neither or both of |
Example
from py_simple import compare_json
diff = compare_json(
data1={"name": "Sara", "age": 25},
data2={"name": "Sara", "age": 26, "city": "NYC"}
)
print(diff)
# {"added": {"city": "NYC"}, "removed": {}, "changed": {"age": (25, 26)}}
def compare_dicts(d1, d2, path=""):
result = {"added": {}, "removed": {}, "changed": {}}
all_keys = set(d1.keys()) | set(d2.keys())
for key in all_keys:
current_path = f"{path}.{key}" if path else key
if key not in d2:
result["removed"][current_path] = d1[key]
elif key not in d1:
result["added"][current_path] = d2[key]
elif d1[key] != d2[key]:
result["changed"][current_path] = (d1[key], d2[key])
return result
flatten_json(seperator='-', data=None, filepath=None)
Flattens a nested dictionary or JSON file into a single-level
dictionary, joining nested keys with seperator.
Handles dicts nested inside dicts, lists nested inside dicts, and
dicts nested inside lists, at any depth. List items are joined
using their index (e.g. b-0, b-1). Provide exactly one of
data or filepath — not both.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
seperator
|
str
|
String used to join nested keys together. Defaults to "-". |
'-'
|
data
|
dict
|
A dictionary to flatten. |
None
|
filepath
|
str
|
Path to a JSON file to flatten. |
None
|
Returns:
| Type | Description |
|---|---|
dict | None
|
dict | None: A single-level dictionary with all nested values unwrapped into flat, uniquely-named keys. |
Raises:
| Type | Description |
|---|---|
EasyJsonError
|
If neither or both of |
Example
from py_simple import flatten_json
flatten_json(data={"a": 1, "b": {"c": 2}})
# -> {"a": 1, "b-c": 2}
from benedict import benedict
d = benedict({"a": 1, "b": {"c": 2}})
flat = dict(d.flatten("-"))
# still need to manually unwrap dicts/lists further
get_json_keys(data=None, filepath=None)
Returns the top-level keys in a dictionary or JSON file.
Provide exactly one of data or filepath. This is useful when
you want to quickly see what information a JSON object contains before
reading a specific value.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
dict
|
A dictionary whose keys should be returned. |
None
|
filepath
|
str
|
Path to a JSON file whose top-level keys should be returned. |
None
|
Returns:
| Type | Description |
|---|---|
list[str]
|
list[str]: The dictionary's top-level keys, in their original order. |
Raises:
| Type | Description |
|---|---|
EasyJsonError
|
If neither or both of |
Example
from py_simple.easy_json import get_json_keys
get_json_keys(data={"name": "Sara", "active": True})
# -> ["name", "active"]
data = {"name": "Sara", "active": True}
keys = list(data.keys())
get_nested(data, path, default=None)
Safely retrieve a value from nested JSON/dict/list using dot notation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
dict | list
|
Parsed JSON data. |
required |
path
|
str
|
Dot-separated path (e.g., "user.address.city" or "items.0.name"). |
required |
default
|
Any
|
Value to return if path not found. Defaults to None. |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
Any |
Value at the path, or default. |
is_json_file(filepath)
Checks whether a filepath points to an existing file with a .json
extension.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
filepath
|
str
|
Path to check. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
bool |
bool
|
True if the file exists and ends in |
Example
from py_simple import is_json_file
if is_json_file("config.json"):
print("Looks good!")
import os
filepath = "config.json"
if os.path.isfile(filepath) and filepath.split(".")[-1] == "json":
print("Looks good!")
is_nested_json(data=None, filepath=None)
Checks whether a dictionary or JSON file contains any nested dictionaries or lists at the top level.
Provide exactly one of data or filepath — not both.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
dict
|
A dictionary to check for nested structures. |
None
|
filepath
|
str
|
Path to a JSON file to check for nested structures. |
None
|
Returns:
| Type | Description |
|---|---|
bool | None
|
bool | None: True if any top-level value is a dict or list, False if all top-level values are flat. |
Raises:
| Type | Description |
|---|---|
EasyJsonError
|
If neither or both of |
Example
from py_simple import is_nested_json
is_nested_json(data={"a": 1, "b": {"c": 2}}) # -> True
is_nested_json(data={"a": 1, "b": 2}) # -> False
data = {"a": 1, "b": {"c": 2}}
is_nested = any(
isinstance(v, (dict, list)) for v in data.values()
)
open_json(filepath)
Opens a JSON file and returns its contents as a dictionary.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
filepath
|
str
|
Path to the JSON file to open. |
required |
Returns:
| Type | Description |
|---|---|
dict | None
|
dict | None: The parsed JSON contents as a dictionary. |
Example
from py_simple import open_json
data = open_json("config.json")
print(data["name"])
import json
try:
with open("config.json", encoding="utf-8") as json_file:
data = json.load(json_file)
print(data["name"])
except Exception as e:
print(f"Couldn't read the file: {e}")
pretty_json(data=None, filepath=None)
Returns a pretty-printed, indented JSON string from a dictionary or
a JSON file. Provide exactly one of data or filepath — not both.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
dict
|
A dictionary to format as pretty-printed JSON. |
None
|
filepath
|
str
|
Path to a JSON file to load and format as pretty-printed JSON. |
None
|
Example
from py_simple import pretty_json
print(pretty_json(data={"name": "Sara"}))
import json
print(json.dumps({"name": "Sara"}, indent=2))
save_json_data(filepath, data)
Saves a dictionary to a JSON file. Raises an error if the file already exists, so you don't accidentally overwrite something.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
filepath
|
str
|
Path to the JSON file to create. |
required |
data
|
dict
|
The data to save. |
required |
Example
from py_simple import save_json_data
save_json_data("config.json", {"name": "Sara"})
import json
import os
filepath = "config.json"
if os.path.exists(filepath):
print(f"File {filepath} already exists.")
else:
with open(filepath, "w") as json_file:
json.dump({"name": "Sara"}, json_file, indent=4)
update_json(filepath, new_data)
Updates a JSON file with new data, merging it into what's already
there. Existing top-level keys in new_data overwrite matching keys
in the file; anything else in the file is left untouched.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
filepath
|
str
|
Path to the JSON file to update. |
required |
new_data
|
dict
|
The data to merge into the existing file. |
required |
Example
from py_simple import update_json
update_json("config.json", {"name": "Sara"})
import json
with open("config.json") as json_file:
data = json.load(json_file)
data.update({"name": "Sara"})
with open("config.json", "w") as json_file:
json.dump(data, json_file, indent=4)