🔗 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.

FunctionPurposeExampleResult
json.dumps(obj)Convert Python object to JSON stringjson.dumps({"name": "Alice"})'{"name": "Alice"}'
json.loads(s)Parse JSON string to Python objectjson.loads('{"age": 30}'){'age': 30}
json.dump(obj, file)Write JSON to filejson.dump(data, open("file.json", "w"))File created
json.load(file)Read JSON from filejson.load(open("file.json"))Python object
dumps(indent=2)Pretty-print JSONjson.dumps(data, indent=2)Formatted string
dumps(sort_keys=True)Sort keys in outputjson.dumps(data, sort_keys=True)Sorted JSON
loads() + try/exceptSafe JSON parsingHandle JSONDecodeErrorError handling
dumps(cls=Encoder)Custom serializationUse custom encoder classExtended 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.

python
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?

😔Poor
🙁Fair
😊Good
😄Great
🤩Excellent