Python JSON – Custom Class Deserialization

Learn to deserialize JSON string and convert deserialized JSON data to a custom class (e.g. User) by extending JSONDecoder or object_hook method.

In Python custom deserialization example, we have following User class. Here, birthdate of type datetime and we can define any formatter string for it.

import datetime

class User:

  def __init__(self, id, name, birthdate):
    self.id = id
    self.name = name
    if isinstance(birthdate, str):
      self.birthdate = datetime.datetime.strptime(birthdate, "%d %b %y")
    else:
      self.birthdate = birthdate

1. Python Custom Deserializer Method using object_hook

1.1. Add Deserializer Method to the Class

By default, json.load() or json.loads() methods read the JSON and return as python dictionary object. In the deserializer method, we have to convert the dict to our custom class.

The given to_object() method takes a dictionary method argument and creates a new User instance by calling its constructor.

Notice how we are using the '__class__' and '__module__' attributes to validate that we are decoding the JSON to correct class in the correct module. The class and module information, in this case, is part of JSON data itself.

We are free to customize the deserialization logic as we need in the application.

class User:

  def __init__(self, id, name, birthdate):
    self.id = id
    self.name = name
    if isinstance(birthdate, str):
      self.birthdate = datetime.datetime.strptime(birthdate, "%d %b %y")
    else:
      self.birthdate = birthdate

  def to_object(d):
    if d['__class__'] == 'User' and d['__module__'] == 'user':
      inst = User(d['id'], d['name'], d['birthdate'])
    else:
      inst = d
    return inst

1.2. Use ‘object_hook’ attribute on json.loads()

Next, to use this deserializer method, use the object_hook attribute in json.loads() method. It will be automatically called by python while converting JSON to the complex object.

import json
from user import User
 
# Python dict
user_json = """{
    "name": "Lokesh",
    "id": 39,
    "birthdate": "06 Jan 91",
    "__class__": "User",
    "__module__": "user"
}"""

# Use object_hook to execute custom deserialization code

user_object = json.loads(user_json, object_hook=User.to_object)

# Verify if we read the valid JSON and created the User object as desired
print(user_object)
print(json.dumps(user_object, default=User.to_dict))

Program output.

<user.User object at 0x000001A92F13AD90>
{"id": 39, "name": "Lokesh", "birthdate": "06 Jan 91", "__class__": "User", "__module__": "user"}

2. Python Custom Deserialization using JSONDecoder

Another way, to add custom deserialization logic, is to extend the JSONDecoder class.

The following ComplexDecoder class declares the dict_to_object() method which provides the logic to convert the JSON to the complex class.

The dict_to_object() method checks all dictionary objects read by json.loads() method and checks the '__class__' and '__module__' properties in dictionary.

If it finds the class and module information, dict_to_object() method imports and loads the class. It then creates a new instance of the class by calling its constructor and passing dictionary key-value pairs as constructor arguments.

import json

class ComplexDecoder(json.JSONDecoder):

  def __init__(self):
    json.JSONDecoder.__init__(
      self,
      object_hook=self.dict_to_object,
    )

  def dict_to_object(self, d):
    if '__class__' in d:
      class_name = d.pop('__class__')
      module_name = d.pop('__module__')
      module = __import__(module_name)
      class_ = getattr(module, class_name)
      args = {
        key: value
        for key, value in d.items()
      }
      inst = class_(**args)
    else:
      inst = d
    return inst

1.2. Use ‘cls’ attribute on json.dumps()

To use custom JSONDecoder, use the cls attribute in json.loads() method. It will be automatically called by python while converting the JSON to the complex object.

import json
from complex_decoder import ComplexDecoder
from user import User
 
# Python dict
user_json = """{
    "name": "Lokesh",
    "id": 39,
    "birthdate": "06 Jan 91",
    "__class__": "User",
    "__module__": "user"
}"""

# Use ComplexDecoder to execute custom deserialization code

user_object = json.loads(user_json, cls=ComplexDecoder)

print(user_object)
print(json.dumps(user_object, default=User.to_dict))

Program output.

<user.User object at 0x0000019533E96880>
{"id": 39, "name": "Lokesh", "birthdate": "06 Jan 91", "__class__": "User", "__module__": "user"}

Happy Learning !!

Was this post helpful?

Join 7000+ Awesome Developers

Get the latest updates from industry, awesome resources, blog updates and much more.

* We do not spam !!

Leave a Comment

HowToDoInJava

A blog about Java and related technologies, the best practices, algorithms, and interview questions.