Creating Custom Serialization Tags
Flask uses the TaggedJSONSerializer to handle session data, allowing it to support types that standard JSON cannot, such as datetime, UUID, and bytes. By implementing a custom JSONTag, you can extend this system to support your own Python classes, ensuring they are serialized and deserialized correctly when stored in the session.
In this tutorial, you will create a custom tag for a Point class and register it with a Flask application.
Prerequisites
To follow this tutorial, you need Flask installed in your environment.
pip install Flask
1. Define the Custom Class
First, create a simple Python class that you want to store in the session.
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f"Point({self.x}, {self.y})"
2. Implement the JSONTag Subclass
To support this class in the serializer, you must create a subclass of flask.json.tag.JSONTag. You need to define a unique key and implement three methods: check, to_json, and to_python.
from flask.json.tag import JSONTag
class TagPoint(JSONTag):
# Conventionally, keys start with a space to avoid collisions
# with standard dictionary keys.
key = " pt"
def check(self, value):
"""Return True if the value is an instance of Point."""
return isinstance(value, Point)
def to_json(self, value):
"""Convert the Point instance into a JSON-serializable list."""
return [value.x, value.y]
def to_python(self, value):
"""Reconstruct the Point instance from the JSON-serializable list."""
return Point(*value)
The to_json method converts your object into a standard JSON type (like a list or dict). The to_python method receives that same data and should return a new instance of your class.
3. Register the Tag with Flask
Flask's session interface uses a default instance of TaggedJSONSerializer. You can access it via app.session_interface.serializer and register your new tag.
from flask import Flask, session
app = Flask(__name__)
app.secret_key = "development key"
# Register the custom tag
app.session_interface.serializer.register(TagPoint)
4. Verify the Serialization
Now you can use the Point class directly in the Flask session. Flask will automatically use your TagPoint to serialize the object when saving the session and deserialize it when loading.
@app.route("/set")
def set_point():
# Store the custom object in the session
session["location"] = Point(10, 20)
return "Point stored in session!"
@app.route("/get")
def get_point():
# Retrieve the custom object from the session
point = session.get("location")
return f"Retrieved: {point}"
When you visit /set, Flask calls TagPoint.to_json. The resulting session cookie will contain a representation like {" pt": [10, 20]}. When you visit /get, Flask identifies the " pt" key and calls TagPoint.to_python to return a Point instance.
Handling Nested Objects
If your custom class contains other objects that might also need tagging (like a Point containing a UUID), use self.serializer.tag() within your to_json method to ensure recursive tagging.
def to_json(self, value):
# Recursively tag x and y in case they are non-standard types
return [self.serializer.tag(value.x), self.serializer.tag(value.y)]
Controlling Tag Precedence
If you are registering a tag for a class that is a subclass of another tagged type (for example, an OrderedDict which is also a dict), use the index parameter in register() to ensure your tag is checked first.
# Insert at the beginning of the tag order to ensure it matches before 'dict'
app.session_interface.serializer.register(TagOrderedDict, index=0)
By default, new tags are appended to the end of the order list in TaggedJSONSerializer. Using index=0 ensures your custom logic takes precedence over the default_tags like TagDict.