Serialize and Deserialize complex JSON in Python
JSON stands for JavaScript Object Notation. It is a lightweight, language-independent format for storing and transferring data often used in web APIs, configuration files and databases.
In Python, JSON is widely used to store data in files or exchange data between systems.
Example:
{
"id": 101,
"company": "GeeksForGeeks
}
Complex JSON Objects
A complex JSON object is a JSON structure that contains nested objects, arrays or a mix of both inside another object. Letâs explore some examples:
1. Nested JSON Objects: An object inside another object.
{
"student": {
"name": "John",
"age": 20
}
}
2. JSON Objects with Arrays: An object that contains a list.
{
"fruits": ["apple", "banana", "mango"]
}
3. Array of JSON Objects: A list where each item is an object.
[
{ "name": "James" },
{ "name": "Jennie" }
]
4. Mixed Nested Objects and Arrays: Objects and arrays combined.
{
"class": [
{ "name": "Sam", "marks": [85, 90, 88] },
{ "name": "Lily", "marks": [92, 87, 91] }
]
}
Serialization and Deserialization
- Serialization means converting a Python object into a format (like JSON) that can be stored or sent over a network using json.dumps().
- Deserialization means converting that stored or transmitted JSON back into a Python object using json.loads().
Letâs understand this with an examples.
1. Working with Simple JSON Objects
When data stored in a Python object only contains basic types (strings, numbers, booleans, etc.), it can be easily serialized and deserialized using Pythonâs built-in json module and objectâs __dict__ attribute.
Example: In this example, a simple object containing only first and last names is serialized and then deserialized.
import json
class GFG_User:
def __init__(self, first_name: str, last_name: str):
self.first_name = first_name
self.last_name = last_name
user = GFG_User(first_name="Jake", last_name="Doyle")
# Serialize â JSON string
json_data = json.dumps(user.__dict__)
print(json_data)
# Deserialize â Python object
data_dict = json.loads(json_data)
new_user = GFG_User(**data_dict)
print(new_user)
Output
{"first_name": "Jake", "last_name": "Doyle"} <__main__.GFG_User object at 0x7fed8f4de850>
Explanation:
- class GFG_User: Defines a class to store user details.
- __init__ method: Initializes first_name and last_name attributes.
- GFG_User(): Creates an object with given names.
- user.__dict__: Gets objectâs attributes as a dictionary.
- json.dumps(user.__dict__): Converts dictionary to a JSON string.
- json.loads(json_data): Converts JSON string back to a dictionary.
- GFG_User(**data_dict): Creates a new object using unpacked dictionary.
2. Handling Complex JSON Objects
When dealing with nested data where an object contains other objects the __dict__ trick no longer works directly. This is because json.dumps() doesnât know how to serialize custom objects inside other objects.
Letâs explore this problem and see how to fix it.
Example: This example creates a Team object that contains multiple Student objects. We try to serialize it using json.dumps(team.__dict__) without any custom handling.
from typing import List
import json
class Student:
def __init__(self, first_name: str, last_name: str):
self.first_name = first_name
self.last_name = last_name
class Team:
def __init__(self, students: List[Student]):
self.students = students
student1 = Student("Geeky", "Guy")
student2 = Student("GFG", "Rocks")
team = Team([student1, student2])
# Try serialization
json_data = json.dumps(team.__dict__, indent=4)
print(json_data)
Output
TypeError: Object of type Student is not JSON serializable
json.dumps() doesnât know how to convert a Student object to JSON automatically. We can fix this by passing a default parameter that tells JSON how to handle unknown objects.
json_data = json.dumps(team.__dict__, default=lambda o: o.__dict__, indent=4)
Example: Here, we use default parameter to handle nested objects during serialization, then deserialize the JSON back to a Python object.
from typing import List
import json
class Student(object):
def __init__(self, first_name: str, last_name: str):
self.first_name = first_name
self.last_name = last_name
class Team(object):
def __init__(self, students: List[Student]):
self.students = students
student1 = Student(first_name="Geeky", last_name="Guy")
student2 = Student(first_name="GFG", last_name="Rocks")
team = Team(students=[student1, student2])
# Serialization
json_data = json.dumps(team, default=lambda o: o.__dict__, indent=4)
print(json_data)
# Deserialization
decoded_team = Team(**json.loads(json_data))
print(decoded_team)
Output
{
"students": [
{
"first_name": "Geeky",
"last_name": "Guy"
},
{
"first_name": "GFG",
"last_name": "Rocks"
}
]
}
__main__.Team object at 0x105cd41d0
Explanation:
- typing.List: for type hints.
- Student class: Stores a studentâs first and last name
- Team class: Holds a list of Student objects.
- default=lambda o: o.__dict__: converts objects into dictionaries.
- indent=4: formats JSON nicely.
- json.loads(): converts JSON string to dictionary.