I am trying to add some overloading to the Flask JSON encoder/decoder to add datetime encoding/decoding but only succeeded through a 'hack'.
from flask import Flask, flash, url_for, redirect, render_template_string
from flask.json import JSONEncoder, JSONDecoder
template = """
<!DOCTYPE html>
<html><head><title>Test JSON encoder/decoder</title></head><body>
{% with messages = get_flashed_messages(with_categories=true) %}{% if messages %}{% for message in messages %}
<p>Flash: {{ message }}</p>
{% endfor %}{% endif %}{% endwith %}
<p>Flash should be: ['Flash message', 'success']</p>
<p><a href="{{ url_for('index') }}">Try again</a></p>
</body></html>
"""
class CustomJSONEncoder(JSONEncoder):
""" Do nothing custom json encoder """
def default(self, obj):
# My custom logic here
# ...
# or
return super(CustomJSONEncoder, self).defaults(obj)
class CustomJSONDecoder(JSONDecoder):
""" Do nothing custom json decoder """
def __init__(self, *args, **kargs):
_ = kargs.pop('object_hook', None)
super(CustomJSONDecoder, self).__init__(object_hook=self.decoder, *args, **kargs)
def decoder(self, d):
# My custom logic here
# ...
# or
return d
app = Flask(__name__, static_url_path='')
app.config['SECRET_KEY'] = 'secret-key'
app.json_encoder = CustomJSONEncoder
app.json_decoder = CustomJSONDecoder
@app.route('/')
def index():
flash('Flash message', 'success')
return redirect(url_for('display'))
@app.route('/b')
def display():
return render_template_string(template)
if __name__ == '__main__':
app.run(debug=True, port=5200)
The hack is that I should copy some code from the Flask.sessions.TaggedJSONSerializer like that:
import uuid
from base64 import b64decode
from werkzeug.http import parse_date
from markupsafe import Markup
from flask._compat import iteritems
class CustomJSONDecoder(JSONDecoder):
""" Do nothing custom json decoder """
def __init__(self, *args, **kargs):
_ = kargs.pop('object_hook', None)
super(CustomJSONDecoder, self).__init__(object_hook=self.decoder, *args, **kargs)
def decode(self, d):
# My custom logic here
# ...
# Copy of the code from Flask.sessions.TaggedJSONSerializer(object).loads(self, value).object_hook(obj)
if len(d) == 1:
the_key, the_value = next(iteritems(d))
if the_key == ' t':
return tuple(the_value)
elif the_key == ' u':
return uuid.UUID(the_value)
elif the_key == ' b':
return b64decode(the_value)
elif the_key == ' m':
return Markup(the_value)
elif the_key == ' d':
return parse_date(the_value)
return d
Do I do it 'correctly' or there is something that I miss?
You can use the functionality of the base class by explicitly calling it's default() method. I have done that in my custom JSONEncoder successfully:
class CustomJSONEncoder(JSONEncoder):
def default(self, obj):
# Calling custom encode function:
jsonString = HelperFunctions.jsonEncodeHandler(obj)
if (jsonString != obj): # Encode function has done something
return jsonString # Return that
return JSONEncoder.default(self, obj) # else let the base class do the work
In the decoder, however, you should remember the object hook passed to the __init__()
function and call it from your own hook:
class CustomJSONDecoder(JSONDecoder):
def __init__(self, *args, **kwargs):
self.orig_obj_hook = kwargs.pop("object_hook", None)
super(CustomJSONDecoder, self).__init__(*args,
object_hook=self.custom_obj_hook, **kwargs)
def custom_obj_hook(self, dct):
# Calling custom decode function:
dct = HelperFunctions.jsonDecodeHandler(dct)
if (self.orig_obj_hook): # Do we have another hook to call?
return self.orig_obj_hook(dct) # Yes: then do it
return dct # No: just return the decoded dict
BTW: You have a typo in your decoder: the object hook you register in the base class is named self.decoder
, but the member is defined as def decode(...)
(without the r at the end). In your example, you register an empty hook and the decode()
should never get called.
Note that you have to tell your flask application what encoder it is gonna use :
app.json_encoder = CustomJSONEncoder
this solved my problem .
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With