Flask: Making an object JSON Serializable

Flask: Making an object JSON Serializable

I was going through some old code the other day and ran across one an old rabbit trail into making objects serializable in JSON. Now to backup and give some context, I was somewhat new to the Flask framework at the time and was trying to pass around a number of customized object models at the Session level. There are a number of reasons why you shouldn’t do this, but hindsight is 20/20.

If this is what you are trying to do, do yourself a favor and look into doing this server side with Redis instead. I’d suggest this simple 4-step process

  1. Roll up a redis:alpine image in Docker
  2. pip install flask-redis
  3. Use Mark Richman’s Flask Redis session interface: https://github.com/mrichman/flask-redis
  4. Profit!?

If you are determined to move on, you’ll need to sub class JSONEncoder and JSONDecoder from the json module. Consider the following module example for a Flask project:

import json
from importlib import import_module
from bson import ObjectId
from app.models.math import SubtractionProblem

class SessionJSONEncoder(json.JSONEncoder):
    def default(self, object):

        data = {
                '__class__': object.__class__.__name__,
                '__module__': object.__module__,
        if isinstance(object, SubtractionProblem):
            obj_dict = {'operands': object.__dict__['_operands'],
                        'operation': object.__dict__['_operation']}
        if isinstance(object, ObjectId):
            obj_dict = {'oid': str(object)}

        return data

class SessionJSONDecoder(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(module_name)
            print('MODULE: {}'.format(module))
            class_ = getattr(module, class_name)
            print('CLASS: {}'.format(class_))
            args = dict(
                (key, value) for key, value in d.items())
            print('INSTANCE ARGS: {}'.format(args))
            inst = class_(**args)
            inst = d
        return inst

The downside here is that all custom objects need to be defined in your JSONEncoder subclass. You’ll also need to make sure that your object constructor can take the objects serialized dictionary and using them as parameters to your class constructor, recreate that class in the JSONDecoder subclass.

Some examples of how to call:

session['my_object'] = SessionJSONEncoder().encode(my_custom_object)

Then later on (presumably after redirect), you can do the following:

my_custom_object = SessionJSONDecoder().decode(session['my_object'])

Then feel free to use the deserialized object as you’d expect.

If you’d like to understand a little bit more about other folks issues with this problem, check out: https://stackoverflow.com/questions/18478287/making-object-json-serializable-with-regular-encoder

Otherwise, happy serializing/deserializing!