ducin
ducin

Reputation: 26437

python structure JSON serializing issue

I've got a cache dictionary that stores a 3-element list for each key: [value, date_created, hits]. The cache communication is done via JSON. There is a items command in the cache that shall return all items. This is the set cache method:

@status
def handle_set(self, key, value):
    self.data[key] = [value, datetime.datetime.now(), 0]
    return

The problem occurs when I have a non-empty cache and I call items on it. Python datetime object is not serializable:

Traceback (most recent call last):
  File "/usr/lib/python2.7/dist-packages/twisted/python/log.py", line 84, in callWithLogger
    return callWithContext({"system": lp}, func, *args, **kw)
  File "/usr/lib/python2.7/dist-packages/twisted/python/log.py", line 69, in callWithContext
    return context.call({ILogContext: newCtx}, func, *args, **kw)
  File "/usr/lib/python2.7/dist-packages/twisted/python/context.py", line 118, in callWithContext
    return self.currentContext().callWithContext(ctx, func, *args, **kw)
  File "/usr/lib/python2.7/dist-packages/twisted/python/context.py", line 81, in callWithContext
    return func(*args,**kw)
--- <exception caught here> ---
  File "/usr/lib/python2.7/dist-packages/twisted/internet/selectreactor.py", line 146, in _doReadOrWrite
    why = getattr(selectable, method)()
  File "/usr/lib/python2.7/dist-packages/twisted/internet/tcp.py", line 460, in doRead
    rval = self.protocol.dataReceived(data)
  File "./server.py", line 17, in dataReceived
    result = getattr(self.factory, command)(**request)
  File "./server.py", line 35, in execute
    return json.dumps(result)
  File "/usr/lib/python2.7/json/__init__.py", line 231, in dumps
    return _default_encoder.encode(obj)
  File "/usr/lib/python2.7/json/encoder.py", line 201, in encode
    chunks = self.iterencode(o, _one_shot=True)
  File "/usr/lib/python2.7/json/encoder.py", line 264, in iterencode
    return _iterencode(o, 0)
  File "/usr/lib/python2.7/json/encoder.py", line 178, in default
    raise TypeError(repr(o) + " is not JSON serializable")
exceptions.TypeError: datetime.datetime(2013, 10, 26, 11, 38, 42, 348094) is not JSON serializable

I have found a similar SO question. But the thing I don't like in the accepted answer is that I have to provide a custom serializer.

in my cache I have different commands, but I'd like to use one JSON-formatting method for all cache commands. I'm afraid I'd have to do an if-cascade if I were to follow that answer.

Is there any way to override datetime.datetime to provide one simple method that will be used by JSON serializer? Or any better solution to this?

datetime.datetime JSON serialization could be as simple as str(d) for me (a string representation).

Upvotes: 0

Views: 448

Answers (1)

Krishan Gupta
Krishan Gupta

Reputation: 3716

The generally accepted approach is to subclass the default encoder.

import json

class CustomJSONEncoder(json.JSONEncoder):
    def default(self, obj):
        if hasattr(obj, 'isoformat'): #handles both date and datetime objects
            return obj.isoformat()
        elif hasattr(obj, 'total_seconds'): #handles both timedelta objects
            return str(obj)
        if isinstance(obj, Decimal): #handles decimal timedelta objects
            return float(obj)
        else:
            return json.JSONEncoder.default(self, obj)

To use the new class:

context['my_json_data'] = json.dumps(my_python_data, cls=CustomJSONEncoder)

The issue is that there is no universally accepted way to represent a date, so Python forces you to choose. While it can be frustrating, Python is forcing you to accept the fact that any time you convert a date you're making a choice.

Upvotes: 1

Related Questions