Skip to main content

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 (via TagDict and PassDict)
  • list (via PassList)
  • tuple (via TagTuple, key: ' t')
  • bytes (via TagBytes, key: ' b')
  • markupsafe.Markup (via TagMarkup, key: ' m')
  • uuid.UUID (via TagUUID, key: ' u')
  • datetime.datetime (via TagDateTime, key: ' d')

Troubleshooting

  • Duplicate Keys: Registering a tag with a key that already exists will raise a KeyError. Use force=True in register() 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_json method calls self.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.