Python
dictionary
string conversion
json
data serialization

Convert a python dict to a string and back

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Converting a Python dictionary to a string and back is a common task for data storage, network transmission, and logging. The two main approaches are json.dumps() / json.loads() for JSON-compatible data, and str() / ast.literal_eval() for Python-literal representations. JSON is the standard choice for interoperability with other languages and systems, while ast.literal_eval() handles Python-specific types that JSON cannot represent.

Method 1: json.dumps() and json.loads()

python
1import json
2
3data = {"name": "Alice", "age": 30, "scores": [95, 87, 92]}
4
5# Dict → string
6json_str = json.dumps(data)
7print(json_str)        # '{"name": "Alice", "age": 30, "scores": [95, 87, 92]}'
8print(type(json_str))  # <class 'str'>
9
10# String → dict
11restored = json.loads(json_str)
12print(restored)        # {'name': 'Alice', 'age': 30, 'scores': [95, 87, 92]}
13print(type(restored))  # <class 'dict'>
14
15# Pretty-printed string
16pretty = json.dumps(data, indent=2, sort_keys=True)
17print(pretty)
18# {
19#   "age": 30,
20#   "name": "Alice",
21#   "scores": [95, 87, 92]
22# }

json.dumps() serializes a dict to a JSON-formatted string. json.loads() parses a JSON string back into a dict. This is the recommended approach for most use cases.

Method 2: str() and ast.literal_eval()

python
1import ast
2
3data = {"name": "Alice", "age": 30, "active": True, "scores": (95, 87)}
4
5# Dict → string
6str_repr = str(data)
7print(str_repr)
8# "{'name': 'Alice', 'age': 30, 'active': True, 'scores': (95, 87)}"
9
10# String → dict
11restored = ast.literal_eval(str_repr)
12print(restored)        # {'name': 'Alice', 'age': 30, 'active': True, 'scores': (95, 87)}
13print(type(restored))  # <class 'dict'>

str() converts a dict to its Python literal representation. ast.literal_eval() safely evaluates that string back into a Python object. Unlike eval(), literal_eval only accepts literals (strings, numbers, tuples, lists, dicts, booleans, None) and cannot execute arbitrary code.

Method 3: repr() and ast.literal_eval()

python
1import ast
2
3data = {"key": "value with 'quotes'", "count": 42}
4
5# repr() produces a string that can be pasted into Python code
6repr_str = repr(data)
7print(repr_str)  # "{'key': \"value with 'quotes'\", 'count': 42}"
8
9# Round-trip back to dict
10restored = ast.literal_eval(repr_str)
11print(restored == data)  # True

repr() is similar to str() but is more explicit about escaping, making it more reliable for round-tripping.

JSON vs ast.literal_eval Comparison

python
1import json
2import ast
3
4# JSON cannot handle tuples — they become lists
5data = {"coords": (10, 20), "active": True}
6
7json_str = json.dumps(data)
8json_restored = json.loads(json_str)
9print(json_restored)  # {'coords': [10, 20], 'active': True}  — tuple became list
10
11str_repr = str(data)
12ast_restored = ast.literal_eval(str_repr)
13print(ast_restored)   # {'coords': (10, 20), 'active': True}  — tuple preserved
14
15# JSON cannot handle sets
16data2 = {"tags": {"python", "coding"}}
17# json.dumps(data2)  # TypeError: Object of type set is not JSON serializable
18
19str_repr2 = str(data2)
20ast_restored2 = ast.literal_eval(str_repr2)
21print(ast_restored2)  # {'tags': {'python', 'coding'}}  — set preserved
Featurejsonstr + ast.literal_eval
InteroperabilityWorks across languagesPython only
Tuple supportConverts to listPreserved
Set supportNot supportedPreserved
None handlingConverts to nullPreserved as None
SpeedFaster (C implementation)Slower (parser)
SafetySafeSafe (no code execution)

Handling Non-Serializable Types

python
1import json
2from datetime import datetime
3
4data = {
5    "user": "Alice",
6    "created": datetime(2025, 1, 15, 10, 30),
7    "scores": {95, 87, 92}
8}
9
10# Custom encoder for non-serializable types
11class CustomEncoder(json.JSONEncoder):
12    def default(self, obj):
13        if isinstance(obj, datetime):
14            return obj.isoformat()
15        if isinstance(obj, set):
16            return list(obj)
17        return super().default(obj)
18
19json_str = json.dumps(data, cls=CustomEncoder)
20print(json_str)
21# '{"user": "Alice", "created": "2025-01-15T10:30:00", "scores": [95, 87, 92]}'
22
23# Custom decoder
24def custom_decoder(d):
25    for key, value in d.items():
26        if key == "created":
27            d[key] = datetime.fromisoformat(value)
28    return d
29
30restored = json.loads(json_str, object_hook=custom_decoder)
31print(type(restored["created"]))  # <class 'datetime.datetime'>

File Storage

python
1import json
2
3data = {"users": [{"name": "Alice"}, {"name": "Bob"}]}
4
5# Write dict to JSON file
6with open("data.json", "w") as f:
7    json.dump(data, f, indent=2)
8
9# Read dict from JSON file
10with open("data.json", "r") as f:
11    loaded = json.load(f)
12
13print(loaded == data)  # True

Note the distinction: json.dumps() / json.loads() work with strings, while json.dump() / json.load() work with file objects.

Common Pitfalls

  • Using eval() instead of ast.literal_eval(): eval() executes arbitrary Python code, making it a security vulnerability. ast.literal_eval() only parses literals and is safe to use with untrusted input.
  • JSON converting tuples to lists: json.dumps((1, 2, 3)) produces "[1, 2, 3]", and json.loads returns a list. If tuple identity matters, use ast.literal_eval with str() or convert lists back to tuples after loading.
  • JSON converting integer keys to strings: json.dumps({1: "a"}) produces '{"1": "a"}' because JSON keys must be strings. After json.loads, the key is "1" (string), not 1 (int).
  • Single quotes in JSON strings: JSON requires double quotes. json.loads("{'key': 'value'}") raises JSONDecodeError. Use json.loads('{"key": "value"}') or ast.literal_eval for Python-style strings.
  • Non-serializable objects in json.dumps: Passing a dict containing datetime, set, bytes, or custom objects raises TypeError. Use a custom JSONEncoder subclass or convert these types before serialization.

Summary

  • Use json.dumps() / json.loads() for standard dict-to-string conversion — it is fast, safe, and interoperable
  • Use str() / ast.literal_eval() when you need to preserve Python-specific types like tuples and sets
  • Never use eval() to parse untrusted strings — always use ast.literal_eval() or json.loads()
  • JSON keys are always strings — integer keys become string keys after round-tripping
  • Use json.dump() / json.load() (no "s") for reading and writing JSON files directly
  • Implement a custom JSONEncoder to handle datetime, set, and other non-serializable types

Course illustration
Course illustration

All Rights Reserved.