Skip to main content

Implementing a Custom JSON Provider

To customize how your application handles JSON serialization and deserialization, you can implement a custom JSON provider by subclassing JSONProvider or DefaultJSONProvider. This allows you to support custom data types, integrate third-party libraries like orjson or ujson, or modify the default response formatting.

Extending the Default Provider

The most common task is adding support for custom types while keeping the default Flask JSON behavior. To do this, subclass DefaultJSONProvider and override the default method or the loads method.

from flask import Flask
from flask.json.provider import DefaultJSONProvider

class MyCustomType:
def __init__(self, value):
self.value = value

class CustomProvider(DefaultJSONProvider):
def default(self, o):
if isinstance(o, MyCustomType):
return {"__type__": "MyCustomType", "val": o.value}
return super().default(o)

def loads(self, s, **kwargs):
# Example: adding an object_hook for deserialization
def hook(obj):
if "__type__" in obj and obj["__type__"] == "MyCustomType":
return MyCustomType(obj["val"])
return obj

kwargs.setdefault("object_hook", hook)
return super().loads(s, **kwargs)

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

Integrating a Third-Party JSON Library

If you want to replace the built-in json module with a faster library like ujson, subclass the base JSONProvider and implement the dumps and loads methods.

import ujson
from flask import Flask
from flask.json.provider import JSONProvider

class UJSONProvider(JSONProvider):
def dumps(self, obj, **kwargs):
return ujson.dumps(obj, **kwargs)

def loads(self, s, **kwargs):
return ujson.loads(s, **kwargs)

class MyApp(Flask):
json_provider_class = UJSONProvider

app = MyApp(__name__)

Customizing the JSON Response

You can override the response method to change how jsonify creates the Response object, such as adding custom headers or changing the default mimetype.

from flask import Flask
from flask.json.provider import DefaultJSONProvider

class SecureJSONProvider(DefaultJSONProvider):
def response(self, *args, **kwargs):
resp = super().response(*args, **kwargs)
resp.headers["X-Content-Type-Options"] = "nosniff"
return resp

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

Registration Methods

There are two ways to register your custom provider in this codebase:

  1. Instance Assignment: Assign an instance of your provider to app.json after the app is created. This is useful for quick overrides.
    app = Flask(__name__)
    app.json = CustomProvider(app)
  2. Class Definition: Set json_provider_class on a Flask subclass. This is the recommended approach for libraries or large applications as the app will automatically instantiate it during initialization.
    class MyFlask(Flask):
    json_provider_class = CustomProvider

Troubleshooting

TypeError in response()

The response method (and the _prepare_response_obj helper in JSONProvider) is designed to take either positional arguments or keyword arguments, but not both.

# This will raise a TypeError
app.json.response({"data": 1}, status=200)

If you need to pass extra parameters to the Response constructor, you should call super().response() and then modify the returned object.

Key Sorting Issues

In DefaultJSONProvider, sort_keys is enabled by default (True). If your data contains dictionaries with non-string keys, json.dumps will fail during sorting. You can disable this in your provider:

class NoSortProvider(DefaultJSONProvider):
sort_keys = False

Accessing the App Instance

The provider stores a reference to the application as self._app. This is a weakref.proxy, so it will behave like the app object but won't prevent the app from being garbage collected. Use self._app if you need to check app configuration like self._app.debug.