Bottle framework: how to return datetime in JSON response
Question: When I try to return JSON containing `datetime` value, I'm getting

    
    
      File "/usr/lib/python2.7/json/encoder.py", line 178, in default
        raise TypeError(repr(o) + " is not JSON serializable")
    TypeError: datetime.datetime(2014, 2, 1, 0, 0) is not JSON serializable
    

Which is normal. Is there an easy way to add an object hook to `bottle` like

    
    
    from bson import json_util
    import json
    json.dumps(anObject, default=json_util.default)
    

to get `datetime` values converted?


Answer: Interesting question! I can see a couple of ways of doing this. The one would
be to write a custom plugin that wraps the `JSONPlugin`:

    
    
    from bottle import route, run, install, JSONPlugin
    from bson import json_util
    
    class JSONDefaultPlugin(JSONPlugin):
        def __init__(self):
            super(JSONDefaultPlugin, self).__init__()
            self.plain_dump = self.json_dumps
            self.json_dumps = lambda body: self.plain_dump(body, default=json_util.default)
    

Which can then be used like this:

    
    
    @route('/hello')
    def index(name):
        return {'test': datetime.datetime(2014, 2, 1, 0, 0)}
    
    install(JSONDefaultPlugin())
    run(host='localhost', port=8080)
    

And will give output like this:

    
    
    {"test": {"$date": 1391212800000}}
    

Another, shorter, way is to simply specify the `json_loads` parameter when
instantiating the JSONPlugin class:

    
    
    import json
    from bson import json_util
    
    install(JSONPlugin(json_dumps=lambda body: json.dumps(body, default=json_util.default)))
    

This produces the same result.

### Background

This all makes a little more sense when you look at the [source code for
bottle](https://github.com/defnull/bottle/blob/master/bottle.py) (some parts
removed below for brevity):

    
    
    class JSONPlugin(object):
        name = 'json'
        api  = 2
    
        def __init__(self, json_dumps=json_dumps):
            self.json_dumps = json_dumps
    
        def apply(self, callback, route):
            dumps = self.json_dumps
            if not dumps: return callback
            def wrapper(*a, **ka):
                ... 
    
                if isinstance(rv, dict):
                    ...
                elif isinstance(rv, HTTPResponse) and isinstance(rv.body, dict):
                    rv.body = dumps(rv.body)
                    rv.content_type = 'application/json'
                return rv
    
            return wrapper
    

All we need to do is make sure the call to `dumps` there receives the
`default` keyword argument you wish to provide.

