AI Computer Institute
Expert-curated CS & AI curriculum aligned to CBSE standards. A bharath.ai initiative. About Us

Serialization

📚 Technology⏱️ 19 min read🎓 Grade 8
✍️ AI Computer Institute Editorial Team Updated: August 2026 CBSE-aligned · Peer-reviewed · 19 min read
Content curated by subject matter experts with IIT/NIT backgrounds. All chapters are fact-checked against official CBSE/NCERT syllabi.

Open a cricket score app during an IPL match. The ball is bowled, the app updates instantly: runs, wickets, overs, the batting pair — all refreshed within a second. That data did not teleport from the stadium's scoring computer to your phone. It was packed into a message, sent across the internet, and unpacked again on your screen. This chapter is about exactly that packing-and-unpacking step, which every programmer needs to understand: serialization.

Why a Python object cannot just be "sent"

Suppose a scoring program on a stadium computer is tracking a match using a Python dictionary:

match_score = {
    "team": "India",
    "runs": 187,
    "wickets": 4,
    "overs": 18.3,
    "batting": ["Kohli", "Pant"],
    "is_powerplay": False
}

While this program is running, match_score is not really "text" sitting somewhere neatly. It is a live structure inside the computer's RAM: a block of memory holding a reference to the string "India", another block holding the integer 187, another holding a list object that itself points to two more string objects, and so on — a small web of linked memory addresses that only make sense to a running Python process. If you tried to copy that raw memory and paste it into a WhatsApp message, or write it into a file, or send it to a phone across the internet, it would be meaningless. The receiving computer does not have Python's memory layout; it does not know what a memory address from a different machine even refers to. A live object cannot cross the gap between two computers, or survive the program that created it being closed.

What can cross that gap is a sequence of bytes with an agreed-upon structure — plain text, in the simplest and most common case. So before an object can be saved to a file, sent over a network, or handed to a completely different program (possibly written in a different language), it needs to be converted into that portable, structured text form. That conversion is serialization. Reversing it — turning the text back into a live object your program can work with again — is deserialization.

A non-computing analogy first

Think about how you'd describe your exact seating position on a train to a friend over a phone call versus filling out an IRCTC ticket. In your head, "my seat" is a rich, connected idea: you can picture the coach, the window, the people around you. But to get that idea to someone else, you have to flatten it into a fixed, agreed format: a string of characters like S4-23-LB (coach S4, seat 23, lower berth). Your friend, reading that code, reconstructs a mental picture of where you're sitting — not identical to yours, but equivalent in every detail that matters. The code S4-23-LB is playing the same role JSON text plays for a Python dictionary: a flat, standardized description that anyone following the same rules can rebuild from scratch.

Formal definitions

Serialization is the process of converting a data structure that exists in a program's memory (a dictionary, a list, a custom object) into a sequential format — usually text or bytes — that can be stored or transmitted. Deserialization is the reverse process: reading that stored or transmitted format and reconstructing an equivalent data structure in memory. The two always come as a pair — data that has been serialized is useless until something on the other end knows how to deserialize it back.

JSON: the format almost everything speaks

Python ships with a module called json that serializes to and from JSON (JavaScript Object Notation). Despite the name, JSON is not a Python-specific or even a JavaScript-specific format — it is a plain-text convention that nearly every programming language can read and write: Python, Java, C++, Swift, JavaScript, and the servers behind IRCTC, UPI apps, and cricket-score APIs all use it. That's precisely why it became the dominant format for one program to talk to another: two programs written by completely different teams, in completely different languages, can exchange data as long as both agree to use JSON.

JSON's syntax is deliberately close to a Python dictionary or list, which makes it easy to learn but also easy to mix up with Python syntax. The differences matter:

  • JSON has no distinction between single and double quotes for strings — only double quotes are valid.
  • Python's True, False, and None become JSON's lowercase true, false, and null.
  • Python tuples have no JSON equivalent — they are serialized as ordinary JSON arrays, exactly like lists.
  • JSON has no separate "int vs float" rule of its own the way Python does, but Python's json module preserves the distinction: a whole number stays without a decimal point, a float keeps one.

Worked example: serializing a match record

Let's serialize the dictionary from earlier. It has six keys: team, runs, wickets, overs, batting, and is_powerplay.

import json

match_score = {
    "team": "India",
    "runs": 187,
    "wickets": 4,
    "overs": 18.3,
    "batting": ["Kohli", "Pant"],
    "is_powerplay": False
}

text = json.dumps(match_score)
print(text)
print(type(text))
print(len(text))

Output:

{"team": "India", "runs": 187, "wickets": 4, "overs": 18.3, "batting": ["Kohli", "Pant"], "is_powerplay": false}
<class 'str'>
112

Trace through what happened. json.dumps() ("dump string") walked the dictionary key by key, in insertion order, and wrote each key as a quoted string followed by a colon and its value. The integer 187 was written without quotes (JSON numbers are unquoted). The float 18.3 kept its decimal point. The list ["Kohli", "Pant"] became a JSON array with square brackets, written the same way it would look in Python. And False — capital F, a Python keyword — became false — lowercase, a JSON keyword. The entire six-key dictionary, nested list and all, became one flat string of 112 characters. Critically, type(text) confirms this is now an ordinary Python string. It has no memory of being "a dictionary" anymore; it is just characters, which is exactly what makes it safe to write to a file or send down a network cable.

Worked example: deserializing it back

restored = json.loads(text)
print(restored)
print(type(restored))
print(restored == match_score)

Output:

{'team': 'India', 'runs': 187, 'wickets': 4, 'overs': 18.3, 'batting': ['Kohli', 'Pant'], 'is_powerplay': False}
<class 'dict'>
True

json.loads() ("load string") parsed the text back into a genuine Python dictionary — notice false correctly became Python's False again, and the keys are back to being ordinary Python dict keys. The equality check restored == match_score prints True, because Python's == for dictionaries compares keys and values, not memory identity.

Misconception: "deserializing gives you back the same object"

It does not. restored and match_score are equal in value but they are two separate objects living at two separate places in memory — this matters enormously if the code that receives restored is running on a different computer entirely (which is the whole point of serializing in the first place). You can confirm this with Python's identity operator:

print(restored is match_score)

Output:

False

is checks whether two names point to the exact same object in memory; == checks whether their contents match. Serialization followed by deserialization always produces a new, independent object with equal contents — never the original object itself. This is actually the entire reason the technique is useful: it lets one computer recreate another computer's data without the two ever sharing memory.

A quiet trap: types that don't round-trip

Not every Python type survives the trip unchanged. Tuples are the clearest example:

data = {"openers": ("Rohit", "Gill")}
print(json.dumps(data))

Output:

{"openers": ["Rohit", "Gill"]}

JSON has no concept of a tuple — only arrays. So a Python tuple is serialized as a JSON array, exactly like a list, and when it is deserialized later, it comes back as a list, never as a tuple. If your program depended on openers being immutable (a tuple), that guarantee is silently lost the moment the data is serialized and later reloaded. This is a genuinely common bug: code that works perfectly before saving-and-reloading state can break afterward, because a tuple quietly became a list.

What json.dumps() refuses to serialize

The JSON format only defines a small set of value types: strings, numbers, booleans, null, arrays, and objects (key-value maps). Python's json module can convert Python's str, int, float, bool, None, list, tuple, and dict into these — but nothing else, by default. Try to serialize a custom object:

class Player:
    def __init__(self, name):
        self.name = name

json.dumps({"captain": Player("Rohit")})

Output:

TypeError: Object of type Player is not JSON serializable

json.dumps() has no built-in rule for turning an arbitrary Player instance into JSON text, so it raises a TypeError rather than guessing. The same happens for a Python set: json.dumps({1, 2, 3}) raises TypeError: Object of type set is not JSON serializable, because JSON has arrays but no unordered "set" concept. The fix is to convert the object into something JSON already understands before serializing it — for the Player example, that could be as simple as serializing {"captain": {"name": "Rohit"}} instead, built by hand or via vars(player), which turns an object's attributes into a plain dictionary.

Scaling up: a list of records

Real applications rarely serialize a single value — they serialize collections. Here is an IRCTC-style booking history serialized with indent=2 for readability:

bookings = [
    {"pnr": "4521367890", "train": "12951", "status": "CNF"},
    {"pnr": "4521367891", "train": "12952", "status": "WL/3"}
]
print(json.dumps(bookings, indent=2))

Output:

[
  {
    "pnr": "4521367890",
    "train": "12951",
    "status": "CNF"
  },
  {
    "pnr": "4521367891",
    "train": "12952",
    "status": "WL/3"
  }
]

The outer structure is a JSON array (matching the outer Python list), and each element is a JSON object (matching each inner Python dict). The indent=2 argument only changes whitespace for human readability — it does not change the data. Without it, json.dumps() would produce the exact same information as one compact line with no line breaks, which is what actually gets sent over a network, since extra whitespace is wasted bytes on a live connection.

Saving to a file: dump vs. dumps

The names are easy to confuse, so it's worth being precise. json.dumps() ("dump string") returns a string, which you can then do anything with. json.dump() (no "s") writes directly to an already-open file, skipping the intermediate string:

with open("match.json", "w") as f:
    json.dump(match_score, f)

with open("match.json", "r") as f:
    loaded = json.load(f)

print(loaded["team"], loaded["runs"])

Output:

India 187

The same naming pattern applies on the reading side: json.loads() parses a string you already have in memory, while json.load() reads directly from an open file object. Mixing these up — passing a file object to json.loads(), or a string to json.load() — is one of the most common beginner errors with this module, and it produces a TypeError or AttributeError rather than working data, because each function expects a different kind of input.

Where this shows up outside your own code

When a UPI app shows "Payment successful," the confirmation your phone receives from the bank's server is not a live Python (or Java, or Kotlin) object shared between two machines — it is serialized data, conceptually shaped something like {"status": "SUCCESS", "amount": 500, "vpa": "friend@upi", "txn_time": "2026-08-11T14:32:00"}, sent as text and deserialized by the app into whatever structure its own code uses to display a green tick. The specific fields any real bank uses are private to that bank's system, but the pattern — flatten to text, send, parse back into a usable structure — is exactly what you just did by hand with json.dumps and json.loads. The same pattern underlies an IRCTC ticket status check, a weather app pulling today's forecast, and a multiplayer game synchronizing player positions between phones.

Other formats, briefly

JSON is not the only serialization format, and it isn't always the right one. CSV (comma-separated values) is simpler and better suited to flat, table-shaped data — a class's marksheet with one row per student and one column per subject — but it has no clean way to represent nested structures like the batting list inside match_score; everything is rows and columns, full stop. XML is an older, more verbose tagged-text format (values wrapped in <tag>...</tag> pairs) still used by some legacy and enterprise systems, but it takes noticeably more bytes to say the same thing JSON says. Python also has a module called pickle, which can serialize almost any Python object — including custom classes like Player that json refuses — by converting it to binary bytes rather than readable text. The catch is important: pickle is Python-only (a Java program cannot read pickled bytes), and loading a pickle file from an untrusted source is genuinely dangerous, because unpickling can execute arbitrary code embedded in the file. JSON's restriction to a handful of safe, simple types is not a limitation to work around — it's a deliberate safety feature, which is a large part of why it became the standard for data sent between strangers' computers over the internet.

The full loop, visually

Serialization and Deserialization Computer A Python dict in RAM "team": "India" "runs": 187 "wickets": 4 "overs": 18.3 "batting": [...] "is_powerplay": False Exists only in this program's running memory. serialize() json.dumps() Serialized JSON (a string) saved to disk or sent over a network { "team": "India", "runs": 187, "wickets": 4, "overs": 18.3, "batting": ["Kohli","Pant"], "is_powerplay": false } All six keys, flattened to 112 characters of plain text. deserialize() json.loads() Computer B Python dict in RAM (new object) "team": "India" "runs": 187 "wickets": 4 "overs": 18.3 "batting": [...] "is_powerplay": False Equal in value — not the identical object. restored == match_score → True restored is match_score → False

Trace it yourself

1. What does json.dumps({"scores": (10, 25, 8)}) print, and what Python type would you get back if you deserialized that result with json.loads()?
Answer: {"scores": [10, 25, 8]}. JSON has no tuple type, so the tuple is written as an array; deserializing it back gives a list, not a tuple — the original type is lost.

2. A classmate writes json.dumps({"created_by": open}), trying to store a reference to Python's built-in open function. What happens, and why?
Answer: It raises a TypeError ("Object of type builtin_function_or_method is not JSON serializable"), because a function is not one of the handful of types (str, int, float, bool, None, list, dict) that the JSON format can represent.

3. Does changing the order in which keys were inserted into a Python dictionary change whether json.dumps(dict1) == json.dumps(dict2) for two dictionaries with identical key-value pairs but different insertion order?
Answer: Yes — json.dumps() writes keys in the dictionary's iteration order by default, so two dictionaries with the same pairs but different insertion order can serialize to different strings, even though dict1 == dict2 would still be True as Python dictionaries (dict equality ignores order; string equality does not).

4. You call json.load("match.json") directly on the filename string instead of an open file object. What goes wrong?
Answer: It raises an AttributeError, because json.load() expects a file object (something with a .read() method), not a filename string. You must first open the file — with open("match.json") as f: json.load(f) — or use json.loads() on text you've already read yourself.

Summary

A Python object lives as connected memory addresses inside one running program and cannot be copied directly into a file or sent to another machine. Serialization flattens that object into a portable, structured sequence — almost always text, most commonly JSON — that any program following the same rules can read. Deserialization is the reverse: parsing that text back into a working data structure. In Python, json.dumps()/json.loads() convert to and from strings in memory, while json.dump()/json.load() read and write files directly. The round trip preserves values but not identity (a deserialized object is a new object, equal but not the same one) and not every Python type survives unchanged — tuples become lists, and custom class instances are rejected outright unless you convert them to a plain dict first. JSON deliberately supports only a small, safe set of types, which is exactly what makes it trustworthy enough to be the shared language between apps, banks, ticketing systems, and servers that have never seen each other's source code.

Practice Exercises

Now it is time to practice! Complete these challenges to solidify your understanding:

  • Exercise 1: Write a short program that demonstrates the core concept from this chapter. Test it with at least 3 different inputs.
  • Exercise 2: Find a real-world example where serialization is used in an Indian company (like TCS, Infosys, Flipkart, or ISRO). Write a paragraph explaining the connection.
  • Exercise 3: Create a mind-map connecting serialization to at least 3 other topics you have studied.
← Memory Management: How Computers RememberCSV Processing: Working with Real Data Files →

Found this useful? Share it!

📱 WhatsApp 🐦 Twitter 💼 LinkedIn