How to Parse JSON in Python — json.loads() Guide
What is JSON Parsing in Python?
JSON parsing in Python is the process of converting a JSON-formatted string into native Python data structures. Python's standard library includes the json module, which provides all the tools needed to parse, manipulate, and serialize JSON data without installing any third-party packages.
The json module maps JSON types directly to Python types, making it seamless to work with API responses, configuration files, and data storage formats.
Python json Module Overview
The json module is part of Python's standard library — no pip install required:
import jsonCore Functions
| Function | Purpose |
|---|---|
json.loads() |
Parse a JSON string into a Python object |
json.load() |
Parse a JSON file into a Python object |
json.dumps() |
Convert a Python object to a JSON string |
json.dump() |
Write a Python object to a file as JSON |
json.loads() vs json.load()
json.loads() — Parse from a String
Use json.loads() when you have JSON data as a string:
import json
json_string = '{"name": "Alice", "age": 30, "is_developer": true}'
data = json.loads(json_string)
print(data) # {'name': 'Alice', 'age': 30, 'is_developer': True}
print(type(data)) # <class 'dict'>
print(data["name"]) # Alice
``'
### json.load() — Parse from a File
Use `json.load()` when reading JSON directly from a file:
```python
import json
with open("data.json", "r", encoding="utf-8") as file:
data = json.load(file)
print(data["name"])The key difference is the input source: json.loads() takes a string, while json.load() takes a file object. Both return the same Python data structures.
JSON to Python Type Mapping
When json.loads() converts JSON to Python, types map as follows:
| JSON Type | Python Type | Example |
|---|---|---|
object |
dict |
{"a":1} → {'a': 1} |
array |
list |
[1,2] → [1, 2] |
string |
str |
"hello" → 'hello' |
number (int) |
int |
42 → 42 |
number (float) |
float |
3.14 → 3.14 |
true |
True |
true → True |
false |
False |
false → False |
null |
None |
null → None |
import json
data = json.loads('''
{
"name": "Alice",
"age": 30,
"height": 1.75,
"is_developer": true,
"spouse": null,
"hobbies": ["reading", "hiking"],
"address": {"city": "New York", "zip": "10001"}
}
''')
print(type(data["name"])) # <class 'str'>
print(type(data["age"])) # <class 'int'>
print(type(data["height"])) # <class 'float'>
print(type(data["is_developer"])) # <class 'bool'>
print(type(data["spouse"])) # <class 'NoneType'>
print(type(data["hobbies"])) # <class 'list'>
print(type(data["address"])) # <class 'dict'>Parsing JSON Arrays
JSON arrays become Python lists:
import json
json_array = '["apple", "banana", "cherry"]'
fruits = json.loads(json_array)
print(fruits) # ['apple', 'banana', 'cherry']
print(fruits[1]) # banana
print(len(fruits)) # 3Arrays of objects are common in API responses:
import json
users_json = '''
[
{"id": 1, "name": "Alice", "role": "Engineer"},
{"id": 2, "name": "Bob", "role": "Designer"},
{"id": 3, "name": "Charlie", "role": "Manager"}
]
'''
users = json.loads(users_json)
for user in users:
print(f"{user['id']}: {user['name']} ({user['role']})")
# Output:
# 1: Alice (Engineer)
# 2: Bob (Designer)
# 3: Charlie (Manager)You can also use list comprehensions and other Python idioms:
engineers = [u for u in users if u["role"] == "Engineer"]
print(engineers) # [{'id': 1, 'name': 'Alice', 'role': 'Engineer'}]Parsing Nested JSON
Deeply nested JSON structures are fully preserved:
import json
nested_json = '''
{
"company": {
"name": "TechCorp",
"founded": 2020,
"location": {
"address": "123 Main St",
"city": "San Francisco",
"coordinates": {
"lat": 37.7749,
"lng": -122.4194
}
},
"departments": [
{
"name": "Engineering",
"headcount": 50,
"teams": ["Frontend", "Backend", "DevOps"]
},
{
"name": "Design",
"headcount": 15,
"teams": ["UI", "UX", "Brand"]
}
]
}
}
'''
data = json.loads(nested_json)
print(data["company"]["name"]) # TechCorp
print(data["company"]["location"]["city"]) # San Francisco
print(data["company"]["location"]["coordinates"]["lat"]) # 37.7749
print(data["company"]["departments"][0]["teams"][1]) # Backendobject_hook — Custom Object Decoding
The json.loads() function accepts an object_hook parameter that lets you transform decoded dictionaries into custom Python objects:
import json
from dataclasses import dataclass
@dataclass
class Person:
name: str
age: int
is_developer: bool
def person_decoder(dct):
if "name" in dct and "age" in dct:
return Person(**dct)
return dct
json_data = '{"name": "Alice", "age": 30, "is_developer": true}'
person = json.loads(json_data, object_hook=person_decoder)
print(person) # Person(name='Alice', age=30, is_developer=True)
print(type(person)) # <class '__main__.Person'>
print(person.name) # Alice
print(person.age) # 30Use Case: Parsing Nested Custom Types
The object_hook is called recursively for each JSON object in the tree:
import json
from datetime import datetime
def custom_decoder(dct):
if "__type__" in dct:
type_name = dct["__type__"]
if type_name == "datetime":
return datetime.fromisoformat(dct["value"])
elif type_name == "complex":
return complex(dct["real"], dct["imag"])
return dct
json_data = '''
{
"event": "Conference",
"start": {"__type__": "datetime", "value": "2026-09-20T09:00:00"},
"end": {"__type__": "datetime", "value": "2026-09-22T18:00:00"},
"coefficient": {"__type__": "complex", "real": 3, "imag": 4}
}
'''
data = json.loads(json_data, object_hook=custom_decoder)
print(data["start"]) # 2026-09-20 09:00:00
print(type(data["start"])) # <class 'datetime.datetime'>
print(data["coefficient"]) # (3+4j)
print(type(data["coefficient"])) # <class 'complex'>Datetime Handling
JSON has no native date or datetime type. Common strategies for handling dates:
Strategy 1: Manual Parsing After Load
import json
from datetime import datetime
json_data = '{"name": "Event", "date": "2026-06-21", "timestamp": "2026-06-21T14:30:00"}'
data = json.loads(json_data)
# Parse manually
data["date"] = datetime.strptime(data["date"], "%Y-%m-%d").date()
data["timestamp"] = datetime.fromisoformat(data["timestamp"])
print(data["date"]) # 2026-06-21
print(type(data["date"])) # <class 'datetime.date'>Strategy 2: Using object_hook
import json
from datetime import datetime
def datetime_hook(dct):
for key, value in dct.items():
if isinstance(value, str):
try:
dct[key] = datetime.fromisoformat(value)
except (ValueError, TypeError):
pass
return dct
json_data = '{"created_at": "2026-06-21T14:30:00", "updated_at": "2026-06-21T15:00:00", "name": "test"}'
data = json.loads(json_data, object_hook=datetime_hook)
print(data["created_at"]) # 2026-06-21 14:30:00
print(type(data["created_at"])) # <class 'datetime.datetime'>Strategy 3: Custom JSON Encoder/Decoder (Round-trip)
For full round-trip support, create a custom encoder and decoder:
import json
from datetime import datetime, date
class DateTimeEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, (datetime, date)):
return {"__type__": "datetime", "value": obj.isoformat()}
return super().default(obj)
def datetime_decoder(dct):
if dct.get("__type__") == "datetime":
return datetime.fromisoformat(dct["value"])
return dct
# Serialize
data = {"event": "Conference", "date": datetime(2026, 6, 21, 14, 30)}
json_str = json.dumps(data, cls=DateTimeEncoder)
print(json_str)
# {"event": "Conference", "date": {"__type__": "datetime", "value": "2026-06-21T14:30:00"}}
# Deserialize
restored = json.loads(json_str, object_hook=datetime_decoder)
print(restored["date"]) # 2026-06-21 14:30:00
print(type(restored["date"])) # <class 'datetime.datetime'>json.dumps() — Serialization
Convert Python objects back to JSON strings:
import json
data = {
"name": "Alice",
"age": 30,
"skills": ["Python", "Django", "PostgreSQL"],
"is_active": True,
"metadata": None,
}
json_string = json.dumps(data)
print(json_string)
# {"name": "Alice", "age": 30, "skills": ["Python", "Django", "PostgreSQL"], "is_active": true, "metadata": null}Pretty Printing with indent
The indent parameter produces human-readable output:
pretty_json = json.dumps(data, indent=2)
print(pretty_json)
# {
# "name": "Alice",
# "age": 30,
# "skills": [
# "Python",
# "Django",
# "PostgreSQL"
# ],
# "is_active": true,
# "metadata": null
# }Sorting Keys
sorted_json = json.dumps(data, indent=2, sort_keys=True)
print(sorted_json)
# {
# "age": 30,
# "is_active": true,
# "metadata": null,
# "name": "Alice",
# "skills": [...]
# }Controlling Whitespace with separators
For compact output (minified):
compact = json.dumps(data, separators=(",", ":"))
print(compact)
# {"name":"Alice","age":30,"skills":["Python","Django","PostgreSQL"],"is_active":true,"metadata":null}File Read/Write
Writing JSON to a File
import json
data = {
"users": [
{"id": 1, "name": "Alice"},
{"id": 2, "name": "Bob"},
],
"metadata": {"version": "1.0", "exported": "2026-06-21"},
}
with open("output.json", "w", encoding="utf-8") as file:
json.dump(data, file, indent=2, ensure_ascii=False)Reading JSON from a File
import json
try:
with open("output.json", "r", encoding="utf-8") as file:
data = json.load(file)
print(f"Loaded {len(data['users'])} users")
except FileNotFoundError:
print("File not found")
except json.JSONDecodeError as e:
print(f"Invalid JSON: {e}")Handling Non-ASCII Characters
The ensure_ascii=False parameter preserves Unicode characters instead of escaping them:
data = {"name": "José", "city": "São Paulo"}
# Default: escapes non-ASCII
print(json.dumps(data))
# {"name": "Jos\u00e9", "city": "S\u00e3o Paulo"}
# With ensure_ascii=False
print(json.dumps(data, ensure_ascii=False, indent=2))
# {
# "name": "José",
# "city": "São Paulo"
# }Error Handling
json.JSONDecodeError
Invalid JSON raises json.JSONDecodeError (a subclass of ValueError):
import json
def safe_loads(json_string):
try:
return json.loads(json_string)
except json.JSONDecodeError as e:
print(f"JSON parse error at line {e.lineno}, column {e.colno}: {e.msg}")
print(f"Problematic text: {e.doc[max(0, e.pos-20):e.pos+20]}")
return None
# Test with various errors
test_cases = [
'{"name": "Alice",}', # trailing comma
"{'name': 'Alice'}", # single quotes
'{"name": "Alice"', # missing closing brace
'{"name": "Alice", "age": }', # missing value
'', # empty string
]
for test in test_cases:
result = safe_loads(test)
if result is None:
print("→ Failed to parse\n")Type Validation After Parsing
import json
def parse_object(json_string):
try:
data = json.loads(json_string)
if not isinstance(data, dict):
raise ValueError("Expected a JSON object (dict)")
return data
except (json.JSONDecodeError, ValueError) as e:
print(f"Error: {e}")
return {}
# Usage
data = parse_object('{"key": "value"}') # OK
data = parse_object('[1, 2, 3]') # Error: Expected a JSON objectAdvanced Usage
Parsing Large Files with ijson
For very large JSON files that do not fit in memory, use the ijson library for streaming:
pip install ijsonimport ijson
with open("huge-file.json", "r", encoding="utf-8") as f:
# Stream objects one at a time
for item in ijson.items(f, "item"):
print(f"Processing record: {item['id']}")Using JSON Pointer (RFC 6901)
The jsonpointer library lets you navigate JSON with pointer expressions:
pip install jsonpointerimport json
import jsonpointer
data = json.loads('''
{
"users": [
{"name": "Alice", "address": {"city": "NYC"}},
{"name": "Bob", "address": {"city": "SF"}}
]
}
''')
city = jsonpointer.resolve_pointer(data, "/users/0/address/city")
print(city) # NYCjson.tools Module
Python's json.tool module provides command-line JSON validation and pretty printing:
# Validate and pretty print
echo '{"name": "Alice"}' | python -m json.tool
# From a file
python -m json.tool input.json
# Minify output
python -m json.tool input.json --compactCommon Pitfalls
Trailing Commas
Python allows trailing commas in dicts/lists, but JSON does not:
# INVALID JSON — will raise json.JSONDecodeError
json.loads('{"name": "Alice", "age": 30,}')
# Fix: strip trailing comma before parsing
import re
def fix_json(json_string):
return re.sub(r",(s*[}]])", r"\1", json_string)
cleaned = fix_json('{"name": "Alice", "age": 30,}')
data = json.loads(cleaned)
print(data) # {'name': 'Alice', 'age': 30}Single Quotes
JSON requires double quotes. Python's json module does not accept single quotes:
# INVALID — single quotes
json.loads("{'name': 'Alice'}") # json.JSONDecodeError
# VALID
json.loads('{"name": "Alice"}') # OKDuplicate Keys
JSON does not strictly forbid duplicate keys, but behavior is undefined. Python keeps the last value:
data = json.loads('{"a": 1, "a": 2}')
print(data["a"]) # 2 (last value wins)Non-JSON Serializable Types
Some Python types cannot be serialized to JSON:
import json
from datetime import datetime
# This will raise TypeError
try:
json.dumps({"now": datetime.now()})
except TypeError as e:
print(f"Cannot serialize: {e}")
# Fix: use a custom encoder or convert to string first
json.dumps({"now": datetime.now().isoformat()}) # OKPerformance Tips
1. Use json.loads() Over eval()
Never use eval() or ast.literal_eval() to parse JSON. json.loads() is faster, safer, and properly validates input.
2. Specify encoding Explicitly
Always specify encoding="utf-8" when opening JSON files to avoid platform-dependent behavior:
with open("data.json", "r", encoding="utf-8") as f:
data = json.load(f)3. Reuse File Handles
For multiple reads, keep the file handle open rather than reopening:
with open("data.json", "r", encoding="utf-8") as f:
data1 = json.load(f) # First parse
# Note: you cannot json.load() twice from same stream
# Reset to read again if needed:
f.seek(0)
data2 = json.load(f)4. Use orjson for Extreme Performance
For high-throughput applications, consider orjson — a Rust-backed JSON library that is significantly faster:
pip install orjsonimport orjson
data = orjson.loads(b'{"name": "Alice", "age": 30}')
print(data) # {'name': 'Alice', 'age': 30}
# Serialize
json_bytes = orjson.dumps(data)
print(json_bytes) # b'{"name":"Alice","age":30}'5. Batch Processing
When processing many small JSON strings, batch them to reduce function call overhead:
import json
json_strings = ['{"id": 1}', '{"id": 2}', '{"id": 3}']
# Parse all at once
parsed = [json.loads(s) for s in json_strings]LangStop JSON Tools
- JSON Formatter — Pretty print and beautify JSON
- JSON Validator — Check JSON syntax and validate
- JSON to Python — Convert JSON to Python dict literal
- CSV to JSON — Convert CSV data to JSON format
- JSON Minifier — Compress JSON for production
- JSON Fix — Auto-correct malformed JSON
- JSON Diff — Compare JSON objects side by side
- JSON to CSV — Convert JSON to spreadsheet format