I have some objects I want to send to celery tasks on my application. Those objects are obviously not json serializable using the default json library. Is there a way to make celery serialize/de-serialize those objects with custom JSON Encoder
/Decoder
?
Asked
Active
Viewed 1.8k times
47

Rafael S. Calsaverini
- 13,582
- 19
- 75
- 132
1 Answers
73
A bit late here, but you should be able to define a custom encoder and decoder by registering them in the kombu serializer registry, as in the docs: http://docs.celeryproject.org/en/latest/userguide/calling.html#serializers.
For example, the following is a custom datetime serializer/deserializer (subclassing python's builtin json module) for Django:
myjson.py (put it in the same folder of your settings.py file)
import json
from datetime import datetime
from time import mktime
class MyEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, datetime):
return {
'__type__': '__datetime__',
'epoch': int(mktime(obj.timetuple()))
}
else:
return json.JSONEncoder.default(self, obj)
def my_decoder(obj):
if '__type__' in obj:
if obj['__type__'] == '__datetime__':
return datetime.fromtimestamp(obj['epoch'])
return obj
# Encoder function
def my_dumps(obj):
return json.dumps(obj, cls=MyEncoder)
# Decoder function
def my_loads(obj):
return json.loads(obj, object_hook=my_decoder)
settings.py
# Register your new serializer methods into kombu
from kombu.serialization import register
from .myjson import my_dumps, my_loads
register('myjson', my_dumps, my_loads,
content_type='application/x-myjson',
content_encoding='utf-8')
# Tell celery to use your new serializer:
CELERY_ACCEPT_CONTENT = ['myjson']
CELERY_TASK_SERIALIZER = 'myjson'
CELERY_RESULT_SERIALIZER = 'myjson'

tbodt
- 16,609
- 6
- 58
- 83

f.cipriani
- 3,357
- 2
- 26
- 22
-
Where should we put this code? Where do we import it? – Eduard Luca Sep 08 '14 at 08:38
-
@EduardLuca are you using Django? Please see my edit – f.cipriani Sep 08 '14 at 09:56
-
Cool. I had to pull out the `register` call from `settings.py` (for some reason it threw a totally non-related error), but that worked. Thanks! – Eduard Luca Sep 08 '14 at 11:17
-
1There is a typo: 'epoc' should be 'epoch' on lines 10 and 20 – xxx Oct 16 '14 at 13:10
-
It will be nice to check `Promise` instances as well, i.e. gettext_lazy objects in `MyEncoder.default` method. `from django.utils.functional import Promise` – Alexandr Bulanov Feb 17 '16 at 12:52
-
I ended up having to do: ```def my_loads(obj): return json.loads(six.text_type(obj), object_hook=my_decoder)``` otherwise, it complains about getting a buffer instead of a string. – LarrikJ Aug 30 '17 at 17:39
-
How to add custom serializer for sending and receiving numpy arrays? – The Gr8 Adakron Jul 27 '18 at 05:41
-
@TheGr8Adakron you can use the same pattern with methods available in another library. Exercise for the reader :) – floer32 Sep 13 '18 at 19:03
-
Another note, in the Django case, another spot where most of this could live instead of a `settings.py` file is in the `celery_app.py` before `Celery` is initialized. (Of course, the `CELERY_*` settings will remain in the settings file, but the rest can move if that seems like tidier organization for your project.) – floer32 Sep 13 '18 at 19:04
-
3This used to work perfectly fine, but since celery 4.2 I cannot get it to work. Error when starting celery worker: Unrecoverable error: ContentDisallowed('Refusing to deserialize untrusted content of type json (application/json)',) – gabn88 Sep 21 '18 at 18:27
-
1Kombu `utils.json.py` now provides, [`register_type`](https://github.com/celery/kombu/blob/35f24f1f01c7895926a990be0616a4582374749a/kombu/utils/json.py#L95). Pretty straightforward. See [here](https://docs.celeryq.dev/projects/kombu/en/latest/userguide/serialization.html#:~:text=If%20you%20need%20support%20for%20custom%20types%2C%20you%20can%20write%20serialize/deserialize%20functions%20and%20register%20them%20as%20follows%3A) for usage. – trozzel Mar 08 '23 at 16:32
-
@gabn88 I encountered the same, this is because Celery's heartbeat uses the default json type. So the solution is to still accept that: `CELERY_ACCEPT_CONTENT = ['json', 'myjson']` – MarkM Jun 02 '23 at 10:05