JSON Files
JSON (JavaScript Object Notation) is the lingua franca of data exchange on the web.
Every API speaks JSON, configuration files use it, and it's the go-to format for storing structured
data. Python's built-in json module makes it easy to parse JSON strings, serialize
Python objects, read/write JSON files, and even handle custom types like dates that JSON doesn't
natively support!
Parsing JSON (loads)
The json.loads() function parses a JSON string and returns the corresponding Python
object. JSON objects become Python dicts, arrays become lists, strings stay strings, numbers become
int or float, booleans become True/False, and null becomes None.
Click Run to execute your code
object {} โ dictarray [] โ liststring โ strnumber (int) โ intnumber (float) โ floattrue/false โ True/Falsenull โ None
Serializing to JSON (dumps)
The json.dumps() function converts Python objects to JSON strings. Use
indent for pretty printing, sort_keys for consistent ordering, and
ensure_ascii to control Unicode handling.
Click Run to execute your code
loads() and dumps() = work with stringsload() and dump() = work with filesThe 's' stands for 'string'!
Reading and Writing JSON Files
For files, use json.load() (without 's') to read and json.dump() to
write. These work directly with file objects. The JSON Lines format (one JSON object per line) is
great for log files and streaming data.
Click Run to execute your code
Each line is a valid JSON object. Great for:
- Log files (append new entries)
- Streaming data (process line by line)
- Large datasets (no memory issues)
- Data pipelines (easy to split/merge)
Custom Types and Encoders
JSON only supports basic types. For datetime, Decimal, sets, or custom classes, you need a
custom encoder. Subclass JSONEncoder or use the default parameter. For
decoding custom types, use object_hook.
Click Run to execute your code
TypeError:-
datetime, date, time-
Decimal, complex-
set, frozenset-
bytes, bytearray- Custom class instances
Use a custom encoder to handle these!
Common Mistakes
1. Confusing load/loads and dump/dumps
# Wrong - using loads() with a file!
with open("data.json", "r") as f:
data = json.loads(f) # TypeError!
# Correct - use load() for files
with open("data.json", "r") as f:
data = json.load(f)
# For strings, use loads()
json_string = '{"key": "value"}'
data = json.loads(json_string)
2. Single quotes in JSON
# Wrong - JSON requires double quotes!
bad_json = "{'name': 'Alice'}" # Single quotes!
data = json.loads(bad_json) # JSONDecodeError!
# Correct - use double quotes
good_json = '{"name": "Alice"}'
data = json.loads(good_json)
3. Trailing commas
# Wrong - JSON doesn't allow trailing commas!
bad_json = '{"a": 1, "b": 2,}' # Trailing comma!
data = json.loads(bad_json) # JSONDecodeError!
# Correct - no trailing comma
good_json = '{"a": 1, "b": 2}'
data = json.loads(good_json)
4. Trying to serialize non-JSON types
from datetime import datetime
# Wrong - datetime is not serializable!
data = {"created": datetime.now()}
json.dumps(data) # TypeError!
# Correct - convert to string first
data = {"created": datetime.now().isoformat()}
json.dumps(data) # Works!
# Or use a custom encoder
class DateEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, datetime):
return obj.isoformat()
return super().default(obj)
5. Not handling JSON errors
# Wrong - crashes on invalid JSON!
user_input = request.get_json() # Could be invalid!
data = json.loads(user_input)
# Correct - handle errors
try:
data = json.loads(user_input)
except json.JSONDecodeError as e:
print(f"Invalid JSON: {e}")
data = {}
Exercise: Configuration Manager
Task: Create a configuration manager that loads, updates, and saves settings.
Requirements:
- Load configuration from a JSON file (or use defaults if not exists)
- Provide get and set methods for settings
- Save changes back to the file
- Use pretty printing for readability
Click Run to execute your code
Show Solution
import json
import os
class ConfigManager:
"""Simple configuration manager using JSON."""
def __init__(self, filename, defaults=None):
self.filename = filename
self.config = defaults or {}
self.load()
def load(self):
"""Load config from file, keep defaults if not exists."""
try:
with open(self.filename, "r") as f:
loaded = json.load(f)
self.config.update(loaded)
except FileNotFoundError:
print(f"No config file, using defaults")
except json.JSONDecodeError:
print(f"Invalid JSON, using defaults")
def save(self):
"""Save config to file with pretty printing."""
with open(self.filename, "w") as f:
json.dump(self.config, f, indent=2)
print(f"Saved to {self.filename}")
def get(self, key, default=None):
"""Get a config value."""
return self.config.get(key, default)
def set(self, key, value):
"""Set a config value."""
self.config[key] = value
def __repr__(self):
return json.dumps(self.config, indent=2)
# Test it
defaults = {
"app_name": "MyApp",
"debug": False,
"max_connections": 10
}
config = ConfigManager("settings.json", defaults)
print("Initial config:")
print(config)
print()
# Modify settings
config.set("debug", True)
config.set("theme", "dark")
config.save()
# Reload and verify
config2 = ConfigManager("settings.json")
print("\nReloaded config:")
print(config2)
print(f"Debug: {config2.get('debug')}")
print(f"Theme: {config2.get('theme')}")
# Cleanup
os.remove("settings.json")
Summary
- Parse string:
data = json.loads(json_string) - Serialize to string:
json_str = json.dumps(data) - Read file:
data = json.load(file) - Write file:
json.dump(data, file) - Pretty print:
json.dumps(data, indent=2) - Sort keys:
json.dumps(data, sort_keys=True) - Unicode:
ensure_ascii=Falsefor non-ASCII chars - Custom encoder: Subclass
JSONEncoderor usedefault=func - Custom decoder: Use
object_hook=func - Handle errors: Catch
JSONDecodeError
What's Next?
Congratulations on completing the File Handling module! You've learned to read, write, and manage files in Python. Next, we'll tackle Error Handling - how to gracefully handle exceptions, create custom errors, and write robust code that doesn't crash unexpectedly!
Enjoying these tutorials?