Extending the Tag System
To handle domain-specific Python objects during tagged JSON serialization—such as when storing custom objects in a Flask session—you must implement and register a custom JSONTag.
Creating a Custom JSONTag
To create a custom tag, subclass JSONTag and implement the check, to_json, and to_python methods. The key attribute defines the string prefix used in the serialized JSON to identify the type.
The following example from tests/test_json_tag.py demonstrates a tag for a simple Foo class:
from flask.json.tag import JSONTag, TaggedJSONSerializer
class Foo:
def __init__(self, data):
self.data = data
class TagFoo(JSONTag):
__slots__ = ()
# Conventionally starts with a space to avoid collisions with dict keys
key = " f"
def check(self, value):
"""Return True if the value should be handled by this tag."""
return isinstance(value, Foo)
def to_json(self, value):
"""Convert the Foo object to a JSON-serializable format."""
# Use self.serializer.tag() to recursively tag nested data
return self.serializer.tag(value.data)
def to_python(self, value):
"""Convert the JSON representation back to a Foo object."""
return Foo(value)
# Usage
serializer = TaggedJSONSerializer()
serializer.register(TagFoo)
Registering Tags for Flask Sessions
Flask uses TaggedJSONSerializer internally for its SecureCookieSessionInterface. To support custom types in your application's session, register your tag on the app's session serializer:
from flask import Flask
from my_app.tags import TagFoo
app = Flask(__name__)
app.session_interface.serializer.register(TagFoo)
Handling Subclasses and Priority
Tags are checked in the order they are registered. If you are tagging a type that is a subclass of a type already handled by a default tag (like dict), you must register your tag at an earlier index using the index parameter.
For example, to support collections.OrderedDict (which is a dict subclass), register it at index 0:
from collections import OrderedDict
from flask.json.tag import JSONTag
class TagOrderedDict(JSONTag):
__slots__ = ('serializer',)
key = ' od'
def check(self, value):
return isinstance(value, OrderedDict)
def to_json(self, value):
return [[k, self.serializer.tag(v)] for k, v in value.items()]
def to_python(self, value):
return OrderedDict(value)
# Register at the beginning of the order to override the default dict tag
app.session_interface.serializer.register(TagOrderedDict, index=0)
Built-in Supported Types
Before creating a custom tag, ensure the type isn't already supported by the TaggedJSONSerializer.default_tags. The system natively handles:
dict(viaTagDictandPassDict)list(viaPassList)tuple(viaTagTuple, key:' t')bytes(viaTagBytes, key:' b')markupsafe.Markup(viaTagMarkup, key:' m')uuid.UUID(viaTagUUID, key:' u')datetime.datetime(viaTagDateTime, key:' d')
Troubleshooting
- Duplicate Keys: Registering a tag with a
keythat already exists will raise aKeyError. Useforce=Trueinregister()to overwrite an existing tag. - Recursive Tagging: If your custom object contains nested objects that also need tagging (like a list of UUIDs), ensure your
to_jsonmethod callsself.serializer.tag(value). - Key Collisions: If a standard dictionary contains exactly one key that matches a registered tag key, the serializer automatically wraps it in a
' di'tag to prevent it from being incorrectly interpreted as a tagged object during deserialization.