🔗 JSON Data
JSON (JavaScript Object Notation) is the standard format for data exchange between applications, APIs, and configuration systems. Python's json
module provides powerful tools for converting between Python objects and JSON strings, enabling seamless data communication and storage.
Think of JSON as a universal language for data - it allows different systems and programming languages to share information in a structured, readable format that both humans and computers can easily understand.
import json
# Convert Python data to JSON
data = {
"name": "Alice Johnson",
"age": 30,
"skills": ["Python", "JavaScript", "SQL"],
"active": True,
"salary": None
}
json_string = json.dumps(data)
print("JSON string:")
print(json_string)
# Convert JSON back to Python
json_text = '{"product": "Laptop", "price": 999.99, "available": true}'
python_data = json.loads(json_text)
print("\nPython object:")
print(python_data)
print(f"Product: {python_data['product']}")
print(f"Price: ${python_data['price']}")
# Working with JSON files
config = {
"database": {
"host": "localhost",
"port": 5432,
"name": "myapp"
},
"features": {
"debug": False,
"cache_enabled": True
}
}
# Save to file
with open("config.json", "w") as file:
json.dump(config, file, indent=2)
print("\nConfiguration saved to config.json")
🎯 Understanding JSON Format
JSON provides a standardized way to represent structured data using simple syntax rules that ensure compatibility across different systems.
Data Type Conversion
Understanding the mapping between Python objects and JSON format helps prevent data conversion errors and ensures compatibility.
import json
import datetime
# Demonstrate JSON data type conversions
python_data = {
"string": "Hello World",
"integer": 42,
"float": 3.14159,
"boolean_true": True,
"boolean_false": False,
"null_value": None,
"list": [1, 2, 3, "four"],
"nested_object": {
"inner_key": "inner_value",
"numbers": [10, 20, 30]
}
}
# Convert to JSON and back
json_string = json.dumps(python_data, indent=2)
print("JSON representation:")
print(json_string)
# Parse back to Python
parsed_data = json.loads(json_string)
print(f"\nOriginal type: {type(python_data)}")
print(f"Parsed type: {type(parsed_data)}")
print(f"Data preserved: {python_data == parsed_data}")
# Handling unsupported types
problematic_data = {
"date": datetime.date.today(),
"set": {1, 2, 3},
"tuple": (1, 2, 3)
}
# Solution: Convert unsupported types
safe_data = {
"date": str(problematic_data["date"]),
"set": list(problematic_data["set"]),
"tuple": list(problematic_data["tuple"])
}
json_string = json.dumps(safe_data, indent=2)
print("\nConverted data:")
print(json_string)
⚡ JSON Serialization and Formatting
Serialization controls how Python objects are converted to JSON format, with options for formatting and readability.
Pretty Printing and Formatting
Formatting options improve JSON readability for debugging and human consumption while maintaining data integrity.
import json
data = {
"users": [
{"id": 1, "name": "Alice", "email": "alice@example.com"},
{"id": 2, "name": "Bob", "email": "bob@example.com"}
],
"metadata": {"version": "1.0", "created": "2024-01-15"}
}
# Compact format (default)
compact = json.dumps(data)
print("Compact JSON:")
print(compact[:100] + "...")
# Pretty formatted with indentation
pretty = json.dumps(data, indent=2)
print("\nPretty formatted JSON:")
print(pretty)
# Sort keys for consistent output
sorted_json = json.dumps(data, indent=2, sort_keys=True)
print("\nSorted keys:")
print(sorted_json)
# Custom separators for ultra-compact format
ultra_compact = json.dumps(data, separators=(',', ':'))
print(f"\nUltra-compact: {ultra_compact[:80]}...")
Custom JSON Encoding
Custom encoders handle Python objects that don't naturally serialize to JSON.
import json
import datetime
from decimal import Decimal
class CustomJSONEncoder(json.JSONEncoder):
"""Custom encoder for special Python types"""
def default(self, obj):
if isinstance(obj, datetime.datetime):
return obj.isoformat()
elif isinstance(obj, datetime.date):
return obj.isoformat()
elif isinstance(obj, Decimal):
return float(obj)
elif isinstance(obj, set):
return list(obj)
elif hasattr(obj, '__dict__'):
return obj.__dict__
return super().default(obj)
class Person:
def __init__(self, name, birth_date):
self.name = name
self.birth_date = birth_date
self.created_at = datetime.datetime.now()
# Create test data with special types
person = Person("Alice", datetime.date(1990, 5, 15))
complex_data = {
"person": person,
"price": Decimal("29.99"),
"tags": {"python", "json", "tutorial"},
"timestamp": datetime.datetime.now()
}
# Serialize with custom encoder
json_string = json.dumps(complex_data, cls=CustomJSONEncoder, indent=2)
print("Custom encoded JSON:")
print(json_string)
🚀 Working with JSON Files
File operations enable persistent data storage and configuration management using JSON format.
Reading and Writing JSON Files
File-based JSON operations are essential for configuration management and data persistence.
import json
import os
def save_user_preferences(preferences, filename="user_config.json"):
"""Save user preferences to JSON file"""
try:
with open(filename, 'w') as file:
json.dump(preferences, file, indent=2)
print(f"Preferences saved to {filename}")
return True
except Exception as e:
print(f"Error saving preferences: {e}")
return False
def load_user_preferences(filename="user_config.json"):
"""Load user preferences from JSON file"""
try:
if not os.path.exists(filename):
print(f"No preferences file found. Using defaults.")
return None
with open(filename, 'r') as file:
preferences = json.load(file)
print(f"Preferences loaded from {filename}")
return preferences
except json.JSONDecodeError as e:
print(f"Invalid JSON in {filename}: {e}")
return None
except Exception as e:
print(f"Error loading preferences: {e}")
return None
# Example usage
default_prefs = {
"theme": "dark",
"language": "en",
"notifications": True,
"auto_save": True,
"recent_files": []
}
# Save preferences
save_user_preferences(default_prefs)
# Load preferences
loaded_prefs = load_user_preferences()
if loaded_prefs:
print("Current preferences:")
for key, value in loaded_prefs.items():
print(f" {key}: {value}")
Configuration Management
JSON configuration files provide flexible, human-readable application settings.
import json
class ConfigManager:
"""Manage application configuration with JSON"""
def __init__(self, config_file="app_config.json"):
self.config_file = config_file
self.config = self.load_config()
def load_config(self):
"""Load configuration from file or create default"""
try:
with open(self.config_file, 'r') as file:
return json.load(file)
except FileNotFoundError:
return self.create_default_config()
except json.JSONDecodeError:
print("Invalid JSON in config file. Using defaults.")
return self.create_default_config()
def create_default_config(self):
"""Create default configuration"""
default_config = {
"app": {
"name": "MyApp",
"version": "1.0.0",
"debug": False
},
"database": {
"host": "localhost",
"port": 5432,
"name": "myapp_db"
},
"features": {
"enable_logging": True,
"cache_timeout": 300,
"max_file_size": 10485760 # 10MB
}
}
self.save_config(default_config)
return default_config
def save_config(self, config=None):
"""Save configuration to file"""
if config is None:
config = self.config
try:
with open(self.config_file, 'w') as file:
json.dump(config, file, indent=2)
print(f"Configuration saved to {self.config_file}")
except Exception as e:
print(f"Error saving configuration: {e}")
def get(self, key_path, default=None):
"""Get configuration value using dot notation"""
keys = key_path.split('.')
value = self.config
for key in keys:
if isinstance(value, dict) and key in value:
value = value[key]
else:
return default
return value
def set(self, key_path, value):
"""Set configuration value using dot notation"""
keys = key_path.split('.')
config = self.config
for key in keys[:-1]:
if key not in config:
config[key] = {}
config = config[key]
config[keys[-1]] = value
self.save_config()
# Usage example
config_manager = ConfigManager()
print("Configuration Settings:")
print(f"App name: {config_manager.get('app.name')}")
print(f"Debug mode: {config_manager.get('app.debug')}")
print(f"Database port: {config_manager.get('database.port')}")
print(f"Cache timeout: {config_manager.get('features.cache_timeout')}")
# Update a setting
config_manager.set('app.debug', True)
print(f"Debug mode updated: {config_manager.get('app.debug')}")
Essential JSON Methods and Functions
Understanding core JSON functions enables efficient data serialization and processing in Python applications.
Function | Purpose | Example | Result |
---|---|---|---|
json.dumps(obj) | Convert Python object to JSON string | json.dumps({"name": "Alice"}) | '{"name": "Alice"}' |
json.loads(s) | Parse JSON string to Python object | json.loads('{"age": 30}') | {'age': 30} |
json.dump(obj, file) | Write JSON to file | json.dump(data, open("file.json", "w")) | File created |
json.load(file) | Read JSON from file | json.load(open("file.json")) | Python object |
dumps(indent=2) | Pretty-print JSON | json.dumps(data, indent=2) | Formatted string |
dumps(sort_keys=True) | Sort keys in output | json.dumps(data, sort_keys=True) | Sorted JSON |
loads() + try/except | Safe JSON parsing | Handle JSONDecodeError | Error handling |
dumps(cls=Encoder) | Custom serialization | Use custom encoder class | Extended types |
Hands-on Exercise
Create a simple student record system using JSON. Build functions to save student data to a JSON file, load data from the file, and add new students. Handle file not found errors gracefully.
import json
def save_students(students, filename="students.json"):
# TODO: Save students list to JSON file
# TODO: Handle file writing errors
pass
def load_students(filename="students.json"):
# TODO: Load students from JSON file
# TODO: Return empty list if file doesn't exist
# TODO: Handle JSON parsing errors
pass
def add_student(students, name, age, grade):
# TODO: Create student dictionary
# TODO: Add to students list
pass
def display_students(students):
# TODO: Print all students in a readable format
pass
# TODO: Test your functions
students = load_students()
print(f"Loaded {len(students)} students")
# Add some students
add_student(students, "Alice", 16, "10th")
add_student(students, "Bob", 17, "11th")
add_student(students, "Carol", 15, "9th")
# Display and save
display_students(students)
save_students(students)
print("Students saved to file")
Solution and Explanation 💡
Click to see the complete solution
import json
def save_students(students, filename="students.json"):
# Save students list to JSON file
try:
with open(filename, 'w') as file:
json.dump(students, file, indent=2)
print(f"Successfully saved {len(students)} students to {filename}")
except Exception as e:
print(f"Error saving students: {e}")
def load_students(filename="students.json"):
# Load students from JSON file
try:
with open(filename, 'r') as file:
students = json.load(file)
return students
except FileNotFoundError:
print(f"File {filename} not found. Starting with empty list.")
return []
except json.JSONDecodeError:
print(f"Error reading JSON from {filename}. Starting with empty list.")
return []
def add_student(students, name, age, grade):
# Create student dictionary
student = {
"name": name,
"age": age,
"grade": grade,
"id": len(students) + 1
}
# Add to students list
students.append(student)
print(f"Added student: {name}")
def display_students(students):
# Print all students in a readable format
if not students:
print("No students found.")
return
print("\nStudent Records:")
print("-" * 30)
for student in students:
print(f"ID: {student['id']}, Name: {student['name']}, Age: {student['age']}, Grade: {student['grade']}")
# Test your functions
students = load_students()
print(f"Loaded {len(students)} students")
# Add some students
add_student(students, "Alice", 16, "10th")
add_student(students, "Bob", 17, "11th")
add_student(students, "Carol", 15, "9th")
# Display and save
display_students(students)
save_students(students)
print("Students saved to file")
Key Learning Points:
- 📌 json.dump(): Write Python objects to JSON files
- 📌 json.load(): Read JSON files into Python objects
- 📌 Error handling: Use try/except for FileNotFoundError and JSONDecodeError
- 📌 File operations: Use 'w' for writing, 'r' for reading JSON files
- 📌 Data structure: Use lists and dictionaries for JSON-compatible data
Learn more about package management to handle external libraries and dependencies in your JSON applications.
Test Your Knowledge
Test what you've learned about JSON data handling:
What's Next?
Now that you can handle JSON data effectively, you're ready to explore package management. Learn how to install external libraries, manage dependencies, and create virtual environments for your Python projects.
Ready to continue? Check out our lesson on Package Management.
Was this helpful?
Track Your Learning Progress
Sign in to bookmark tutorials and keep track of your learning journey.
Your progress is saved automatically as you read.