File Serialization
On this page
Working with CSV FilesWriting a CSV FileReading a CSV FileWorking with JSONWriting JSONReading JSONBinary Serialization with PickleSaving Objects with PickleLoading Objects with PickleManaging Paths withpathlibConclusionOnce you understand how to read and write plain text files, the next step is learning how to store and exchange structured data. Real-world applications rarely deal only with raw text—they work with tables, hierarchical records, API responses, configuration files, model outputs, and complex Python objects. Serialization is the process of converting Python data structures into standardized formats, while deserialization restores them back into Python objects.
Python offers several serialization tools, each designed for different needs:
- CSV for simple tabular data
- JSON for structured, language-independent data
- pickle for Python-specific binary serialization
- pathlib for managing file paths; it supports file operations but is not a serialization format
Together, they form the backbone of modern data workflows. Let’s explore each one, starting with the most common: CSV.
Working with CSV Files
CSV (Comma-Separated Values) is the universal language of spreadsheets and datasets. It’s simple, human-readable, and understood by nearly every tool—from Excel to machine learning libraries. Because of this, CSV is often the first step in importing user data or exporting analysis results.
Python’s built-in csv module handles the details of reading and writing these files safely.
Writing a CSV File
Consider a small dataset of classic plays. Writing it to a CSV file is straightforward:
import csv
rows = [
["id", "title", "year"],
[1, "Hamlet", 1603],
[2, "Macbeth", 1606],
]
with open("plays.csv", "w", newline="", encoding="utf-8") as f:
writer = csv.writer(f)
writer.writerows(rows)csv.writer() handles correct CSV formatting, and writerows() sends each list as one table row. The newline="" argument prevents accidental extra blank lines.
Reading a CSV File
Reading the file back follows the same pattern:
with open("plays.csv", "r", newline="", encoding="utf-8") as f:
reader = csv.reader(f)
for row in reader:
print(row)Output
['id', 'title', 'year']
['1', 'Hamlet', '1603']
['2', 'Macbeth', '1606']
Run the writing example first. Each row read back is a list of strings, including numeric-looking fields.
When datasets become larger or column names matter,csv.DictReader allows you to treat each row as a dictionary—making the data much easier to manipulate.
Now that we’ve seen how Python works with flat tables, let’s move to a more flexible and expressive format: JSON.
Working with JSON
While CSV is ideal for simple tables, many applications use nested or hierarchical data: configuration files, web API responses, user accounts, or structured logs. JSON (JavaScript Object Notation) is designed for exactly this kind of data and has become the standard for data exchange across the internet.
Python’s json module makes converting between JSON text and Python objects effortless.
Writing JSON
Here is a structured record describing a book:
import json
book = {
"title": "Pride and Prejudice",
"author": "Jane Austen",
"year": 1813,
"characters": ["Elizabeth Bennet", "Mr. Darcy"]
}
with open("book.json", "w", encoding="utf-8") as f:
json.dump(book, f)json.dump() serializes the Python dictionary into JSON format and writes it to the file.
Reading JSON
Deserializing the data is just as simple:
with open("book.json", "r", encoding="utf-8") as f:
data = json.load(f)
print(data)Output
{'title': 'Pride and Prejudice', 'author': 'Jane Austen', 'year': 1813, 'characters': ['Elizabeth Bennet', 'Mr. Darcy']}
JSON is readable, language-independent, and widely supported—making it the preferred choice for APIs, configuration files, and interoperable data pipelines.
But there are times when neither CSV nor JSON can capture the full richness of Python objects. For those situations, Python provides a different tool: pickling.
Binary Serialization with Pickle
JSON supports a limited set of data types: for example, tuples become JSON arrays and return as lists, while sets and arbitrary custom instances need an explicit conversion. pickle can preserve many Python-specific types for trusted Python-only workflows.
Pickling converts supported objects into a binary stream. Custom classes must remain importable with compatible definitions when loading. Only load trusted pickle data: unpickling can execute code.
Saving Objects with Pickle
import pickle
numbers = {"evens": [2, 4, 6], "odds": [1, 3, 5]}
with open("numbers.pkl", "wb") as f:
pickle.dump(numbers, f)"wb"opens the file in binary write mode.pickle.dump()serializes the object into a compact binary format.
This process is similar to “freezing” the object so it can be reused later.
Loading Objects with Pickle
To restore the saved object back into Python:
with open("numbers.pkl", "rb") as f:
restored = pickle.load(f)
print(restored)Output
{'evens': [2, 4, 6], 'odds': [1, 3, 5]}
After unpickling, you get the same dictionary structure, with the same lists, in the same order—just as it was when saved.
Pickling is extremely convenient for Python-only workflows, such as caching machine-learning models, storing intermediate results, or saving program state. But because the format is binary and Python-specific, it is not appropriate for data meant to be shared with other environments.
The formats define how data is stored. Path management determines where the files are read and written.
Managing Paths with pathlib
As projects grow, files are rarely kept in a single directory. You may need folders for logs, datasets, outputs, or temporary files. Hardcoding paths like "C:\\users\\data\\file.txt" quickly becomes fragile and platform-dependent.
Python’s pathlib module offers a clean, object-oriented way to construct and work with paths across different operating systems.
Example: Creating and Using a Path
from pathlib import Path
base = Path(".") # current directory
data_dir = base / "data" # build a subdirectory path
data_dir.mkdir(exist_ok=True)
file_path = data_dir / "hello.txt"
file_path.write_text("Hello from pathlib!", encoding="utf-8")
print(file_path.read_text(encoding="utf-8"))Output
Hello from pathlib!
/joins paths naturally and readably.mkdir(exist_ok=True)creates the folder if needed.write_text()andread_text()simplify I/O operations.
pathlib makes your code portable, clearer, and much easier to maintain—especially in larger applications or collaborative projects.
Conclusion
Serialization builds on basic file handling to support a range of storage needs: CSV for tabular data, JSON for structured and human-readable formats, pickle for supported Python types in trusted workflows, and pathlib for portable path management.
Together, these tools let programs save results, reload state, and exchange information reliably. With data management covered, the next chapter turns to error handling and testing to ensure your programs remain robust in real-world use.