Parsing nested dictionary strings requires identifying the format first: Python literal syntax uses single quotes and Python keywords (True, None), while JSON uses double quotes and its own keywords (true, null). Using the wrong parser causes errors.
Parse Python Literal Strings with ast.literal_eval()
When the string contains single quotes or Python-specific values like True, False, or None, use the ast.literal_eval() function.
import ast
python_string = "{'user': {'id': 1, 'active': True, 'data': None}}"
data = ast.literal_eval(python_string)
print(data)
print(data['user']['active'])
print(type(data))Handle Deeply Nested Structures
import ast
complex_string = """
{
'level1': {
'level2': {
'level3': {
'value': [1, 2, 3],
'flag': True
}
}
}
}
"""
data = ast.literal_eval(complex_string)
print(data['level1']['level2']['level3']['value'])Parse JSON Strings with json.loads()
When the string uses double quotes and JSON keywords (true, false, null), use the json module.
import json
json_string = '{"user": {"id": 1, "active": true, "data": null}}'
data = json.loads(json_string)
print(data)
print(data['user']['active'])Parse JSON API Responses
import json
api_response = '''
{
"status": "success",
"data": {
"users": [
{"id": 1, "profile": {"name": "Alice", "verified": true}},
{"id": 2, "profile": {"name": "Bob", "verified": false}}
]
}
}
'''
data = json.loads(api_response)
for user in data['data']['users']:
name = user['profile']['name']
verified = user['profile']['verified']
print(f"{name}: {'✓' if verified else '✗'}")Output:
Alice: ✓
Bob: ✗Identify the Correct Format
Examine the string to determine which parser to use.
| Indicator | Format | Parser |
|---|---|---|
Single quotes ('key') |
Python | ast.literal_eval() |
Double quotes ("key") |
JSON | json.loads() |
True / False |
Python | ast.literal_eval() |
true / false |
JSON | json.loads() |
None |
Python | ast.literal_eval() |
null |
JSON | json.loads() |
Auto-Detect and Parse
import ast
import json
def parse_nested_dict(s: str) -> dict:
"""Parse string to dict, auto-detecting format."""
s = s.strip()
try:
return json.loads(s)
except json.JSONDecodeError:
pass
try:
result = ast.literal_eval(s)
if isinstance(result, dict):
return result
raise ValueError("String does not represent a dictionary")
except (ValueError, SyntaxError) as e:
raise ValueError(f"Cannot parse string as dict: {e}")
json_str = '{"nested": {"key": true}}'
print(parse_nested_dict(json_str))
python_str = "{'nested': {'key': True}}"
print(parse_nested_dict(python_str))Avoid the Quote Replacement Trap
Never attempt to convert between formats by replacing quotes. This approach fails with embedded quotes in string values.
import json
python_string = "{'message': \"It's working\"}"
broken = python_string.replace("'", '"')
print(broken)
try:
json.loads(broken)
except json.JSONDecodeError as e:
print(f"Parse failed: {e}")
import ast
data = ast.literal_eval(python_string)
print(data)Handle Parsing Errors
Implement robust error handling for production code.
import ast
import json
def safe_parse_dict(s: str, default: dict = None) -> dict:
"""Safely parse nested dict string with fallback."""
if default is None:
default = {}
if not s or not s.strip():
return default
try:
result = json.loads(s)
return result if isinstance(result, dict) else default
except (json.JSONDecodeError, TypeError):
pass
try:
result = ast.literal_eval(s)
return result if isinstance(result, dict) else default
except (ValueError, SyntaxError):
pass
return default
print(safe_parse_dict('{"a": {"b": 1}}'))
print(safe_parse_dict('not a dict'))
print(safe_parse_dict('invalid', {'fallback': True}))Access Nested Values Safely
Once parsed, access deeply nested values with error handling.
def get_nested(data: dict, *keys, default=None):
"""Safely get nested dictionary value."""
for key in keys:
try:
data = data[key]
except (KeyError, TypeError, IndexError):
return default
return data
config = {
'database': {
'primary': {
'host': 'localhost',
'port': 5432
}
}
}
print(get_nested(config, 'database', 'primary', 'host'))
print(get_nested(config, 'database', 'backup', 'host', default='N/A'))Quick Reference
| String Format | Parser | Example |
|---|---|---|
| Python literals | ast.literal_eval(s) |
"{'key': True}" |
| JSON | json.loads(s) |
'{"key": true}' |
| Unknown format | Try both with error handling | See parse_nested_dict() |
Conclusion
Use ast.literal_eval() for Python-formatted strings from logs, configuration files, or debugging output. Use json.loads() for JSON from web APIs or data interchange. Never attempt manual quote replacement: it creates subtle bugs with special characters. When the format is unknown, implement a parser that tries both methods with proper error handling.