Skip to main content

Built-in Serialization Tags Reference

When you store a tuple, a bytes object, or a datetime in a Flask session, standard JSON serialization would lose the specific Python type information, converting them into lists or strings. Flask solves this by using a tagged serialization system in flask.json.tag that preserves these types across the cookie boundary.

The TaggedJSONSerializer manages a collection of JSONTag subclasses, each responsible for identifying a specific Python type and converting it to a JSON-compatible format marked with a unique key.

Recursive Container Handling

To support nested structures, Flask uses "pass-through" tags that do not add their own keys but ensure that their contents are recursively tagged.

PassList and PassDict

The PassList and PassDict classes are essential for the recursive nature of the serializer. Unlike other tags, they do not have a key attribute. Instead, they override the tag method to return a new list or dictionary where every element has been processed by the serializer.

In PassDict, Flask specifically avoids tagging dictionary keys because JSON objects only support string keys. Only the values are recursively tagged:

class PassDict(JSONTag):
def to_json(self, value: t.Any) -> t.Any:
# JSON objects may only have string keys, so don't bother tagging the
# key here.
return {k: self.serializer.tag(v) for k, v in value.items()}

Built-in Type Tags

Flask provides several built-in tags to handle common Python types that are not natively supported by JSON.

Tuples

The TagTuple class distinguishes Python tuples from lists. It serializes a tuple as a JSON list wrapped in a dictionary with the key " t".

# Python: (1, 2)
# Serialized: {" t": [1, 2]}

Bytes

The TagBytes class handles binary data by encoding it into a base64 string. It uses the key " b".

class TagBytes(JSONTag):
key = " b"

def to_json(self, value: t.Any) -> t.Any:
return b64encode(value).decode("ascii")

def to_python(self, value: t.Any) -> t.Any:
return b64decode(value)

Date and Time

The TagDateTime class uses Werkzeug's http_date and parse_date utilities to convert datetime objects to RFC 822 strings and back. It uses the key " d".

UUIDs

The TagUUID class serializes uuid.UUID objects to their hex string representation using the key " u".

Markup Objects

The TagMarkup class integrates with the MarkupSafe library. It identifies any object with a __html__ method and serializes it to its HTML string. Note that during deserialization, it always returns a markupsafe.Markup instance, regardless of the original object's specific class. It uses the key " m".

Collision Avoidance with TagDict

A potential conflict arises if an application stores a dictionary that happens to have exactly one key matching a registered tag (e.g., {" t": "value"}). To prevent the serializer from misinterpreting this as a tagged tuple, Flask uses TagDict.

TagDict identifies 1-item dictionaries whose key is a registered tag and appends __ to the key during serialization.

class TagDict(JSONTag):
key = " di"

def to_json(self, value: t.Any) -> t.Any:
key = next(iter(value))
return {f"{key}__": self.serializer.tag(value[key])}

def to_python(self, value: t.Any) -> t.Any:
key = next(iter(value))
return {key[:-2]: value[key]}

Tag Registration and Order

The order in which tags are registered in TaggedJSONSerializer is critical because the first tag that returns True for check() will be used. More specific types must be registered before more general types.

For example, TagDict is registered before PassDict in the default_tags list of TaggedJSONSerializer:

default_tags = [
TagDict,
PassDict,
TagTuple,
PassList,
# ... other tags
]

If you were to add support for OrderedDict, you would need to register it at index 0 to ensure it is checked before the standard dict check in PassDict.

from flask.json.tag import JSONTag
from collections import OrderedDict

class TagOrderedDict(JSONTag):
key = ' od'
def check(self, value):
return isinstance(value, OrderedDict)
# ... to_json and to_python implementation

app.session_interface.serializer.register(TagOrderedDict, index=0)