Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow to define a custom JSON encoder #274

Closed
kyouko-taiga opened this issue Jul 7, 2016 · 6 comments
Closed

Allow to define a custom JSON encoder #274

kyouko-taiga opened this issue Jul 7, 2016 · 6 comments
Assignees

Comments

@kyouko-taiga
Copy link

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.

@miguelgrinberg
Copy link
Owner

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.

@kyouko-taiga
Copy link
Author

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?

@miguelgrinberg
Copy link
Owner

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.

@kyouko-taiga
Copy link
Author

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.

@miguelgrinberg
Copy link
Owner

miguelgrinberg commented Jul 7, 2016

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.

@kyouko-taiga
Copy link
Author

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants