Flask-socketio: Allow to define a custom JSON encoder

Created on 7 Jul 2016  路  6Comments  路  Source: miguelgrinberg/Flask-SocketIO

As far as I understood, there's no way to define a custom JSON encoder for SocketIO, so that it overrides the default one.
I saw that it is possible to provide a complete json package as a replacement for Python's default one, but it seems a bit overkill if one simply wants to define how to encode some non-serilizable type.

documentation

Most helpful comment

I think I do understand what you are asking. If you pass flask.json in the json argument as I suggested, then any custom encoder or decoder you added to your Flask app will be used.

Do you want to use a custom JSON encoder that is not the same as what you are using in your Flask app, or maybe you don't have a Flask app at all? That's the only situation in which you would need to create a wrapper for the original json package.

If you wanted to override the default encoder or decoder, then all you need to do is write a module or class with two functions or methods dumps and loads. Maybe something like this:

import json

class MyAwesomeJsonWrapper(object):
    @staticmethod
    def dumps(*args, **kwargs):
        if 'cls' not in kwargs:
            kwargs['cls'] = MyAwesomeEncoder
        return json.dumps(*args, **kwargs)

    @staticmethod
    def loads(*args, **kwargs):
        return json.loads(*args, **kwargs)

So it really isn't a lot of code that you need to write.

All 6 comments

Oh looks like this is a documentation bug. You can pass a json argument with your favorite json implementation. It is one of the lower level engine.io options, it is documented in the engine.io package (here) but looks like I missed it in the main package docs.

Actually I think that is documented in flask-socketio (https://github.com/miguelgrinberg/Flask-SocketIO/blob/master/flask_socketio/__init__.py#L76). But this is what I meant when I said it seems a bit overkill as I don't want to change the json implementation, just its encoder.

Could it be possible for the SocketIO object to use the json encoder defined in the Flask app for instance?

Ah right, so it is documented as a socketio option, but it should be in the engineio section as that is where this is implemented.

In any case, just pass the Flask json module if that's what you want to use. Add from flask import json and then pass json=json as an argument to SocketIO.

Thanks for the quick answers! Obviously I fail to make myself clear.

What I would've wanted is to pass a subclass of JSONEncoder to the SocketIO instance, so that it could encode my custom objects, in the same fashion as I would do it with Flask. For instance:

from flask import Flask
from flask.json import JSONEncoder

class MyAwesomeEncoder(JSONEncoder):
    def default(self, object_):
        ...

app = Flask(__name__)
app.json_encoder = MyAwesomeEncoder

That way, I wouldn't have to write a complete json module, just to handle a handful of objects in my encoder. I can create a module that just forwards everything to Python's json implementation (or any other) and simply redefine loads and dumps to use my custom encoder. But it feels cumbersome to me, and less flexible than passing a subclass.

That said, digging in your code I feel like I should have opened this issue on engine.io rather than here. In particular, this feature would be implemented by the Packet class so that this line would rather look this:

encoded_packet += self.json.dumps(self.data, separators=(',', ':'), cls=self.json_encoder)

Coming back to flask-socketio, it would be awesome if the self.json_encoder in the above line was pointing to the app.json_encoder we would have read in SocketIO.init_app.

By the way, the same argument could be made for the decoder at this line.

I think I do understand what you are asking. If you pass flask.json in the json argument as I suggested, then any custom encoder or decoder you added to your Flask app will be used.

Do you want to use a custom JSON encoder that is not the same as what you are using in your Flask app, or maybe you don't have a Flask app at all? That's the only situation in which you would need to create a wrapper for the original json package.

If you wanted to override the default encoder or decoder, then all you need to do is write a module or class with two functions or methods dumps and loads. Maybe something like this:

import json

class MyAwesomeJsonWrapper(object):
    @staticmethod
    def dumps(*args, **kwargs):
        if 'cls' not in kwargs:
            kwargs['cls'] = MyAwesomeEncoder
        return json.dumps(*args, **kwargs)

    @staticmethod
    def loads(*args, **kwargs):
        return json.loads(*args, **kwargs)

So it really isn't a lot of code that you need to write.

Alright, thanks! Sorry I didn't think setting the JSON encoder on the flask application would actually change the class that gets loaded by flask.json.

Maybe it is worth adding in the documentation as well.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

benjaminturley picture benjaminturley  路  4Comments

nh916 picture nh916  路  3Comments

j2logo picture j2logo  路  3Comments

dlernz picture dlernz  路  4Comments

EndenDragon picture EndenDragon  路  3Comments