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:
- Instance Assignment: Assign an instance of your provider to
app.jsonafter the app is created. This is useful for quick overrides.app = Flask(__name__)
app.json = CustomProvider(app) - Class Definition: Set
json_provider_classon aFlasksubclass. 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.