Working with JSON Data

JSON (JavaScript Object Notation) is a lightweight data format that's easy for humans to read/write and easy for machines to parse/generate. It looks like Python dictionaries with key-value pairs. Python's json module converts between JSON strings and Python objects. Used everywhere: APIs, configuration files, data storage, web communication.

# Working with JSON Data
import json
import pprint

print("WORKING WITH JSON DATA")
print("=" * 50)

# Example 1: Basic JSON operations
print("\n1. BASIC JSON OPERATIONS")
print("-" * 25)

# Sample data as Python dictionary
python_data = {
    "name": "John Doe",
    "age": 30,
    "city": "New York",
    "is_student": False,
    "courses": ["Math", "Science", "English"],
    "grades": {"Math": 90, "Science": 85, "English": 88},
    "contact": {
        "email": "john@example.com",
        "phone": "123-456-7890"
    }
}

print("Python dictionary:")
pprint.pprint(python_data, indent=2)

# Convert Python dictionary to JSON string
print("\nConvert to JSON string:")
json_string = json.dumps(python_data, indent=2)
print(json_string)

# Convert JSON string back to Python dictionary
print("\nConvert back to Python:")
python_dict_again = json.loads(json_string)
print(f"Name: {python_dict_again['name']}")
print(f"Age: {python_dict_again['age']}")
print(f"First course: {python_dict_again['courses'][0]}")

# Example 2: Reading and Writing JSON files
print("\n\n2. READING AND WRITING JSON FILES")
print("-" * 25)

# Write data to JSON file
student_data = {
    "students": [
        {
            "id": 1,
            "name": "Alice",
            "age": 20,
            "major": "Computer Science",
            "grades": {"math": 90, "science": 85, "english": 92}
        },
        {
            "id": 2,
            "name": "Bob",
            "age": 21,
            "major": "Mathematics",
            "grades": {"math": 95, "science": 88, "english": 80}
        },
        {
            "id": 3,
            "name": "Charlie",
            "age": 19,
            "major": "Physics",
            "grades": {"math": 85, "science": 92, "english": 78}
        }
    ],
    "class_name": "Introduction to Programming",
    "instructor": "Dr. Smith",
    "semester": "Spring 2024"
}

# Write to file
with open("students.json", "w") as file:
    json.dump(student_data, file, indent=2)

print("Created students.json file")

# Read from file
with open("students.json", "r") as file:
    loaded_data = json.load(file)

print("\nLoaded data from file:")
print(f"Class: {loaded_data['class_name']}")
print(f"Instructor: {loaded_data['instructor']}")
print(f"Number of students: {len(loaded_data['students'])}")

# Example 3: Processing JSON data
print("\n\n3. PROCESSING JSON DATA")
print("-" * 25)

print("Student Information:")
print("=" * 40)

for student in loaded_data["students"]:
    print(f"\nID: {student['id']}")
    print(f"Name: {student['name']}")
    print(f"Age: {student['age']}")
    print(f"Major: {student['major']}")
    
    # Calculate average grade
    grades = student["grades"]
    avg_grade = sum(grades.values()) / len(grades)
    print(f"Average Grade: {avg_grade:.1f}")
    
    # Find highest grade
    highest_subject = max(grades, key=grades.get)
    print(f"Best Subject: {highest_subject} ({grades[highest_subject]})")

# Example 4: Creating JSON from user input
print("\n\n4. CREATING JSON FROM USER INPUT")
print("-" * 25)

# Simulate user input
def create_student_profile():
    """Create student profile as JSON"""
    print("Create Student Profile")
    print("-" * 20)
    
    # In real program, use input()
    # For demo, using predefined values
    student = {
        "name": input("Enter name: ") or "Emma Wilson",
        "student_id": input("Enter student ID: ") or "S2024001",
        "email": input("Enter email: ") or "emma@university.edu",
        "enrollment_year": int(input("Enter enrollment year: ") or 2023),
        "active": True
    }
    
    # Add courses
    courses = []
    print("\nEnter courses (type 'done' when finished):")
    while True:
        course = input("Course name: ") or "Mathematics"
        if course.lower() == "done":
            break
        credit = int(input("Credits: ") or 3)
        courses.append({"name": course, "credits": credit})
    
    student["courses"] = courses
    
    # Convert to JSON
    student_json = json.dumps(student, indent=2)
    
    print("\nStudent Profile (JSON):")
    print(student_json)
    
    return student

# Create profile
student_profile = create_student_profile()

# Example 5: Working with APIs (simulated)
print("\n\n5. WORKING WITH APIS (SIMULATED)")
print("-" * 25)

# Simulate API response (in real life, you'd use requests library)
api_response_json = '''
{
  "weather": [
    {
      "id": 800,
      "main": "Clear",
      "description": "clear sky",
      "icon": "01d"
    }
  ],
  "main": {
    "temp": 22.5,
    "feels_like": 23.1,
    "temp_min": 20.0,
    "temp_max": 25.0,
    "pressure": 1012,
    "humidity": 65
  },
  "wind": {
    "speed": 3.6,
    "deg": 200
  },
  "name": "London",
  "dt": 1678886400
}
'''

# Parse API response
weather_data = json.loads(api_response_json)

print("Weather API Response:")
print("=" * 30)
print(f"City: {weather_data['name']}")
print(f"Temperature: {weather_data['main']['temp']}°C")
print(f"Feels like: {weather_data['main']['feels_like']}°C")
print(f"Weather: {weather_data['weather'][0]['description']}")
print(f"Humidity: {weather_data['main']['humidity']}%")
print(f"Wind Speed: {weather_data['wind']['speed']} m/s")

# Example 6: Config files in JSON
print("\n\n6. CONFIGURATION FILES")
print("-" * 25)

# Create config
app_config = {
    "app_name": "MyApp",
    "version": "1.0.0",
    "settings": {
        "theme": "dark",
        "language": "en",
        "notifications": True,
        "auto_save": True
    },
    "database": {
        "host": "localhost",
        "port": 5432,
        "username": "admin",
        "max_connections": 10
    },
    "features": ["user_auth", "file_upload", "reports", "analytics"]
}

# Save config
with open("config.json", "w") as config_file:
    json.dump(app_config, config_file, indent=2)

print("Created config.json")

# Load and use config
with open("config.json", "r") as config_file:
    config = json.load(config_file)

print(f"\nApp: {config['app_name']} v{config['version']}")
print(f"Theme: {config['settings']['theme']}")
print(f"Database: {config['database']['host']}:{config['database']['port']}")
print(f"Features: {', '.join(config['features'])}")

# Example 7: JSON validation
print("\n\n7. JSON VALIDATION AND ERROR HANDLING")
print("-" * 25)

# Invalid JSON strings
invalid_json_strings = [
    '{"name": "John", "age": 30,}',  # Trailing comma
    '{"name": "John", "age": }',     # Missing value
    '{"name": "John", age: 30}',     # Unquoted key
    '{"name": "John", "age": 30'     # Missing closing brace
]

print("Testing JSON validation:")
for i, json_str in enumerate(invalid_json_strings, 1):
    print(f"\nTest {i}: {json_str[:30]}...")
    try:
        data = json.loads(json_str)
        print("  ✓ Valid JSON")
    except json.JSONDecodeError as e:
        print(f"  ✗ Invalid JSON: {e.msg}")
        print(f"    Error at position: {e.pos}")

# Example 8: Complex nested JSON
print("\n\n8. COMPLEX NESTED JSON")
print("-" * 25)

# Company organizational structure
company_data = {
    "company": "TechCorp Inc.",
    "departments": [
        {
            "name": "Engineering",
            "manager": "Alice Johnson",
            "teams": [
                {
                    "name": "Frontend",
                    "members": ["Bob", "Charlie", "Diana"],
                    "projects": ["Website Redesign", "Mobile App"]
                },
                {
                    "name": "Backend",
                    "members": ["Eve", "Frank", "Grace"],
                    "projects": ["API Development", "Database Optimization"]
                }
            ]
        },
        {
            "name": "Sales",
            "manager": "Henry Smith",
            "teams": [
                {
                    "name": "Enterprise",
                    "members": ["Ivy", "Jack"],
                    "regions": ["North America", "Europe"]
                }
            ]
        }
    ]
}

# Process nested data
print(f"Company: {company_data['company']}")
print("\nDepartment Structure:")
for dept in company_data["departments"]:
    print(f"\n  Department: {dept['name']}")
    print(f"  Manager: {dept['manager']}")
    print(f"  Teams: {len(dept['teams'])}")
    
    for team in dept["teams"]:
        print(f"    - {team['name']}: {len(team['members'])} members")

# Example 9: Custom JSON encoder/decoder
print("\n\n9. CUSTOM JSON ENCODER")
print("-" * 25)

import datetime

class CustomJSONEncoder(json.JSONEncoder):
    """Custom encoder for special types"""
    def default(self, obj):
        if isinstance(obj, datetime.datetime):
            return obj.isoformat()
        elif isinstance(obj, set):
            return list(obj)
        elif hasattr(obj, '__dict__'):
            return obj.__dict__
        return super().default(obj)

# Data with special types
complex_data = {
    "event": "Conference",
    "date": datetime.datetime(2024, 6, 15, 9, 0, 0),
    "attendees": {"Alice", "Bob", "Charlie"},
    "metadata": {"version": 1, "active": True}
}

print("Original data with special types:")
print(complex_data)

print("\nEncoded with custom encoder:")
encoded = json.dumps(complex_data, cls=CustomJSONEncoder, indent=2)
print(encoded)

# Example 10: Real-world scenario - E-commerce
print("\n\n10. REAL-WORLD: E-COMMERCE ORDER")
print("-" * 25)

order = {
    "order_id": "ORD20240315001",
    "customer": {
        "id": "CUST001",
        "name": "John Smith",
        "email": "john.smith@email.com",
        "address": {
            "street": "123 Main St",
            "city": "New York",
            "state": "NY",
            "zip": "10001"
        }
    },
    "items": [
        {"product_id": "P001", "name": "Laptop", "quantity": 1, "price": 999.99},
        {"product_id": "P002", "name": "Mouse", "quantity": 2, "price": 25.50},
        {"product_id": "P003", "name": "Keyboard", "quantity": 1, "price": 79.99}
    ],
    "payment": {
        "method": "credit_card",
        "transaction_id": "TXN789012",
        "amount": 1130.98,
        "status": "completed"
    },
    "shipping": {
        "method": "express",
        "tracking_number": "TRK123456789",
        "estimated_delivery": "2024-03-18"
    },
    "order_date": "2024-03-15T10:30:00",
    "status": "processing"
}

# Calculate total
items_total = sum(item["quantity"] * item["price"] for item in order["items"])
print(f"Order ID: {order['order_id']}")
print(f"Customer: {order['customer']['name']}")
print(f"\nItems:")
for item in order["items"]:
    total = item["quantity"] * item["price"]
    print(f"  {item['name']} x{item['quantity']}: ${total:.2f}")

print(f"\nSubtotal: ${items_total:.2f}")
print(f"Payment Amount: ${order['payment']['amount']:.2f}")
print(f"Status: {order['status'].upper()}")
print(f"Tracking: {order['shipping']['tracking_number']}")

# Save order to file
with open("order.json", "w") as f:
    json.dump(order, f, indent=2)
print("\nOrder saved to order.json")