Skip to main content

JSON Providers and Customization

Flask abstracts all JSON operations through a provider interface. This design allows applications to customize how data is serialized and deserialized, or even replace the underlying JSON library entirely, while maintaining a consistent API across the framework.

The JSONProvider Interface

The base JSONProvider class, defined in src/flask/json/provider.py, establishes the contract for JSON operations within a Flask application. It maintains a weak reference to the application instance via self._app.

The interface defines five primary methods:

  • dumps(obj, **kwargs): Serializes an object to a JSON string.
  • loads(s, **kwargs): Deserializes a JSON string or bytes to a Python object.
  • dump(obj, fp, **kwargs): Serializes an object and writes it to a file-like object.
  • load(fp, **kwargs): Reads from a file-like object and deserializes the content.
  • response(*args, **kwargs): Creates a Response object containing the JSON-serialized data.

The response method is what powers the flask.json.jsonify function. It uses an internal helper, _prepare_response_obj, to ensure that either positional arguments or keyword arguments are provided, but not both.

# Example of the response logic in JSONProvider
def response(self, *args: t.Any, **kwargs: t.Any) -> Response:
obj = self._prepare_response_obj(args, kwargs)
return self._app.response_class(self.dumps(obj), mimetype="application/json")

Default JSON Behavior

The DefaultJSONProvider is the standard implementation used by Flask. It wraps Python's built-in json module but extends it to support common Python types that are not JSON-serializable by default.

Extended Type Support

The DefaultJSONProvider uses a custom _default function to handle the following types:

  • Dates and Datetimes: Serialized to RFC 822 strings (HTTP date format) using werkzeug.http.http_date.
  • UUIDs and Decimals: Serialized to their string representations.
  • Dataclasses: Automatically converted to dictionaries using dataclasses.asdict.
  • Markup: Objects with an __html__ method (like those from MarkupSafe) are converted to strings.

Configuration Attributes

You can configure the behavior of the default provider by modifying its attributes on the app.json instance:

  • ensure_ascii: Defaults to True. If set to False, non-ASCII characters are not escaped.
  • sort_keys: Defaults to True. Ensures that dictionary keys are sorted alphabetically in the output.
  • compact: Controls whether the output includes indentation and spaces. By default, it is None, which enables pretty-printing in debug mode and compact output otherwise.
  • mimetype: The content type used for responses, defaulting to application/json.

Customizing JSON Operations

There are two primary ways to customize JSON behavior in this codebase: modifying the existing provider or replacing it with a custom subclass.

Modifying the Default Provider

For simple changes, you can modify the attributes of the existing provider instance on the application object:

app = Flask(__name__)
app.json.sort_keys = False
app.json.ensure_ascii = False

Implementing a Custom Provider

For more advanced customization, such as adding an object_hook for deserialization or using a different library like orjson, you can subclass DefaultJSONProvider.

The following example, adapted from tests/test_json.py, demonstrates how to implement a custom object_hook:

from flask.json.provider import DefaultJSONProvider

class CustomProvider(DefaultJSONProvider):
def object_hook(self, obj):
if len(obj) == 1 and "_foo" in obj:
return CustomType(obj["_foo"])
return obj

def loads(self, s, **kwargs):
kwargs.setdefault("object_hook", self.object_hook)
return super().loads(s, **kwargs)

app = Flask(__name__)
app.json = CustomProvider(app)

To use a custom provider globally across all instances of a Flask subclass, you can set the json_provider_class attribute:

class MyFlask(Flask):
json_provider_class = CustomProvider

Global Delegation

The top-level functions in the flask.json module (dumps, loads, jsonify, etc.) are designed to delegate to the current application's provider if an application context is active.

As seen in src/flask/json/__init__.py, these functions check for current_app:

def dumps(obj: t.Any, **kwargs: t.Any) -> str:
if current_app:
return current_app.json.dumps(obj, **kwargs)

# Fallback if no app context is active
kwargs.setdefault("default", _default)
return _json.dumps(obj, **kwargs)

This ensures that any customization applied to app.json is respected by all JSON operations performed within the context of that application, including those triggered by jsonify or when returning a dict from a view function.