1. Introduction
We already got used to creating objects in our programs. Remember, we’re about to dive deeper into classes and objects, but even now we understand that variables, lists, even simple strings — they’re not just random things, they’re entities in our program. For example, we can create a variable int age = 30; or string name = "Vasya";. But what if we need to save to a file info about a whole user who has a name, age, address, a list of favorite books and much more?
Imagine: you’re writing a game. You have an object Player with a bunch of stats: health, level, inventory (a list of items), coordinates on the map, etc. The player plays, levels up, finds cool artifacts. Then they decide to quit the game. What happens? All the data about their adventures that lived in memory disappears! Sad! To avoid that, we need to save the state of the Player object to a file, and when the player returns, load it back.
And that’s where serialization comes in. It’s exactly about building a bridge between "live" objects in memory and "dead" but persistent data on disk.
2. The serialization process (object -> file)
Let’s break down how the "magic" of turning an object into bytes that can be written to a file happens.
Imagine we have this class Book:
// This is our "blueprint" or "plan" for creating book objects
public class Book
{
// Book properties
public string Title { get; set; } // Book title
public string Author { get; set; } // Author
public int Year { get; set; } // Year of publication
// Constructor - special method to create new Book objects
public Book(string title, string author, int year)
{
Title = title;
Author = author;
Year = year;
}
// A method for easy printing of book info (not required for serialization, but useful)
public void DisplayInfo()
{
Console.WriteLine($"Title: {Title}, Author: {Author}, Year: {Year}");
}
}
Step 1: Create an object to serialize.
First, of course, we need the object we want to save. For example, we created an instance of Book:
Book myFavoriteBook = new Book("Avtostopom po Galaktike", "Douglas Adams", 1979);
This object myFavoriteBook is now in RAM.
Step 2: Choose a tool (serializer).
We can’t just "copy" the object to disk. The computer doesn’t understand objects directly in files — it needs bytes. We need a special tool — a serializer. Its job is to break our object into parts (its properties: Title, Author, Year) and turn those parts into a sequence of bytes or a text string (for example JSON or XML).
We won’t dive into concrete implementations today — just keep in mind that it’s a special "converter box".
Step 3: Convert the object into a data stream.
The serializer takes our object myFavoriteBook, looks at its properties (Title, Author, Year) and converts each into a format that can be written. All those bytes (or text chars) are collected into a single data stream — a long "tape" of info.
Step 4: Write the stream to a file.
Now that we have this "tape of bytes", we use our old friends FileStream and maybe StreamWriter (if we chose a text format like JSON) or just FileStream (for pure binary data) to write that stream to disk.
3. The deserialization process (file -> object)
Step 1: Read the data stream from the file.
We again use FileStream and if needed StreamReader (for text formats) to read the file contents. Data comes as a "tape" of bytes or text.
Step 2: Choose a tool (deserializer).
We need the reverse tool — a deserializer. It must know how to interpret the received bytes/text and properly reconstruct the object structure. Very important: for deserialization you use the same kind of serializer (and usually the same library) that you used for serialization, otherwise your "assembler" won’t understand the instructions.
Step 3: Convert the stream back into an object.
The deserializer reads the data, understands where Title is, then Author, then Year, and based on that creates a new Book object in memory, filling its properties.
Step 4: Get the ready object.
Voila! We have a full-fledged Book object back in RAM that we can work with.
Example: Save our "Super-Book" manually (for concept)
For now we won’t use specialized libraries: let’s do the simplest "manual" serialization and deserialization using StreamWriter and StreamReader. This helps understand the idea.
Our Book object:
public class Book
{
public string Title { get; set; }
public string Author { get; set; }
public int Year { get; set; }
public Book(string title, string author, int year)
{
Title = title;
Author = author;
Year = year;
}
public void DisplayInfo()
{
Console.WriteLine($"Title: \"{Title}\", Author: {Author}, Year: {Year}");
}
}
Manual serialization: method SaveBookToTextFile
Create a method that saves the book properties to a text file, one property per line.
void SaveBookToTextFile(Book book, string filePath)
{
using var writer = new StreamWriter(filePath);
writer.WriteLine(book.Title);
writer.WriteLine(book.Author);
writer.WriteLine(book.Year);
}
What happens? We open a StreamWriter and sequentially write Title, Author, Year — that’s our simplest serialization scheme.
If you run the code, the file will contain:
Avtostopom po Galaktike
Douglas Adams
1979
Manual deserialization: method LoadBookFromTextFile
Write a method that reads the data and constructs a new Book object.
Book LoadBookFromTextFile(string filePath)
{
using var reader = new StreamReader(filePath);
string title = reader.ReadLine();
string author = reader.ReadLine();
int year = int.Parse(reader.ReadLine());
return new Book(title, author, year);
}
And use these methods in Main:
//create the object
var myBook = new Book("Avtostopom po Galaktike", "Douglas Adams", 1979);
string filePath = "my_favorite_book.txt";
//save it to a file
SaveBookToTextFile(myBook, filePath);
//read from the file
Book loadedBook = LoadBookFromTextFile(filePath);
What happens in LoadBookFromTextFile? We open a StreamReader and read lines in the same order: first the title, then the author, then the year and parse it with int.Parse. After that we create a new instance of Book.
In practice you should add checks (File.Exists) and error handling with try-catch, but here we focus on the idea itself.
Why manual serialization is bad (and why libraries are needed)
- Lots of manual code. Every property must be written and then read. If there are many objects and fields — the code grows.
- Brittle to changes. Added a new property Pages (int)? You’ll have to change both writing and reading and carefully keep the order.
- Complex structures. Nested collections and objects (for example, List<Chapter>) will turn the code into spaghetti.
- Formats and efficiency. Text format is simple but not always compact or safe; for binary data you’ll have to work with bytes manually, BinaryWriter/BinaryReader etc.
- No metadata. Our file doesn’t "know" that the first line is Title and the third is Year. Specialized serializers can store metadata and be more resilient to model changes.
That’s why real projects use ready-made serializer libraries that can automatically parse/build objects, work with JSON, XML and binary formats and handle model changes more robustly. More on that in the next lecture!
Practical use: why serialization matters
- Saving and loading data. Settings, game state, configurations.
- Data transfer over the network. Exchanging complex objects between services (often in JSON).
- Caching. Fast reuse of previously obtained data.
- Logging complex objects. Useful for debugging and auditing.
- Deep copying objects. Serialization + deserialization as a way to clone an object graph.
Serialization is one of the cornerstones of modern software: from saving game progress to working with web services — it’s everywhere!
GO TO FULL VERSION