JSON and Dictionaries

Converting, Parsing, and Working with JSON Data

Introduction to JSON and Python

JSON (JavaScript Object Notation) is the most common data format for APIs and configuration files. Python dictionaries map naturally to JSON objects, making conversion straightforward with the built-in json module.

Type Mapping

Python → JSON

  • dict → object
  • list, tuple → array
  • str → string
  • int, float → number
  • True/False → true/false
  • None → null

JSON → Python

  • object → dict
  • array → list
  • string → str
  • number → int/float
  • true/false → True/False
  • null → None

Converting Dictionary to JSON

import json

# Basic conversion
data = {
    'name': 'Alice',
    'age': 30,
    'is_active': True,
    'balance': None
}

# Convert to JSON string
json_string = json.dumps(data)
print(json_string)
# {"name": "Alice", "age": 30, "is_active": true, "balance": null}

# Pretty print with indentation
pretty_json = json.dumps(data, indent=2)
print(pretty_json)
# {
#   "name": "Alice",
#   "age": 30,
#   "is_active": true,
#   "balance": null
# }

# Sort keys alphabetically
sorted_json = json.dumps(data, indent=2, sort_keys=True)

# Ensure ASCII (escape non-ASCII characters)
json.dumps({'city': '東京'}, ensure_ascii=True)   # {"city": "\u6771\u4eac"}
json.dumps({'city': '東京'}, ensure_ascii=False)  # {"city": "東京"}

Converting JSON to Dictionary

import json

# Parse JSON string to dictionary
json_string = '{"name": "Alice", "age": 30, "active": true}'
data = json.loads(json_string)

print(data)           # {'name': 'Alice', 'age': 30, 'active': True}
print(type(data))     # <class 'dict'>
print(data['name'])   # Alice

# Handle JSON arrays
json_array = '[{"id": 1}, {"id": 2}]'
items = json.loads(json_array)
print(items)  # [{'id': 1}, {'id': 2}]

Reading and Writing JSON Files

import json

# Write dictionary to JSON file
data = {'users': [{'name': 'Alice'}, {'name': 'Bob'}]}

with open('data.json', 'w') as f:
    json.dump(data, f, indent=2)

# Read JSON file to dictionary
with open('data.json', 'r') as f:
    loaded_data = json.load(f)

print(loaded_data)  # {'users': [{'name': 'Alice'}, {'name': 'Bob'}]}

# Read with encoding (important for non-ASCII)
with open('data.json', 'r', encoding='utf-8') as f:
    data = json.load(f)

Note: Use json.dump() and json.load() for files, and json.dumps() and json.loads() for strings. The "s" stands for "string".

Handling API Responses

import json
# In real code, you'd use: import requests

# Simulated API response
api_response = '''
{
    "status": "success",
    "data": {
        "users": [
            {"id": 1, "name": "Alice", "email": "[email protected]"},
            {"id": 2, "name": "Bob", "email": "[email protected]"}
        ],
        "total": 2
    }
}
'''

# Parse the response
response_data = json.loads(api_response)

# Access nested data safely
users = response_data.get('data', {}).get('users', [])
for user in users:
    print(f"{user['name']}: {user['email']}")

# With requests library (common pattern):
# response = requests.get('https://api.example.com/users')
# data = response.json()  # Automatically parses JSON

Working with Nested JSON

import json

nested_data = {
    'company': {
        'name': 'TechCorp',
        'departments': {
            'engineering': {
                'employees': [
                    {'name': 'Alice', 'role': 'Lead'},
                    {'name': 'Bob', 'role': 'Developer'}
                ]
            },
            'sales': {
                'employees': [
                    {'name': 'Charlie', 'role': 'Manager'}
                ]
            }
        }
    }
}

# Safe nested access with get()
def safe_get(data, *keys, default=None):
    """Safely navigate nested dictionaries."""
    for key in keys:
        if isinstance(data, dict):
            data = data.get(key, default)
        else:
            return default
    return data

# Usage
eng_employees = safe_get(nested_data, 'company', 'departments', 'engineering', 'employees', default=[])
print(eng_employees)
# [{'name': 'Alice', 'role': 'Lead'}, {'name': 'Bob', 'role': 'Developer'}]

# Get non-existent path safely
hr_employees = safe_get(nested_data, 'company', 'departments', 'hr', 'employees', default=[])
print(hr_employees)  # []

Common JSON Errors and Solutions

JSONDecodeError: Invalid JSON

# Problem: Single quotes instead of double quotes
bad_json = "{'name': 'Alice'}"  # Invalid!
# json.loads(bad_json)  # JSONDecodeError

# Solution: Use double quotes
good_json = '{"name": "Alice"}'
data = json.loads(good_json)  # Works!

TypeError: Object not serializable

from datetime import datetime

# Problem: datetime not JSON serializable
data = {'created': datetime.now()}
# json.dumps(data)  # TypeError!

# Solution 1: Convert to string
data = {'created': datetime.now().isoformat()}

# Solution 2: Custom encoder
class DateTimeEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, datetime):
            return obj.isoformat()
        return super().default(obj)

json.dumps({'created': datetime.now()}, cls=DateTimeEncoder)

Trailing Commas

# Problem: Trailing comma (valid in Python, invalid in JSON)
bad_json = '{"name": "Alice", "age": 30,}'  # Invalid!

# Solution: Remove trailing comma
good_json = '{"name": "Alice", "age": 30}'

Best Practices

Always use encoding='utf-8' when reading/writing files

Prevents encoding issues with special characters

Use .get() for safe access to potentially missing keys

Avoids KeyError exceptions with unknown JSON structure

Validate JSON before processing

Use try/except to handle malformed JSON gracefully

Use indent=2 for human-readable output

Makes debugging and logging much easier

On This Page

Related Content

External Resources