How to overcome “datetime.datetime not JSON serializable”?

后端 未结 30 2697
梦谈多话
梦谈多话 2020-11-22 03:31

I have a basic dict as follows:

sample = {}
sample[\'title\'] = \"String\"
sample[\'somedate\'] = somedatetimehere


        
相关标签:
30条回答
  • 2020-11-22 03:44

    Try this one with an example to parse it:

    #!/usr/bin/env python
    
    import datetime
    import json
    
    import dateutil.parser  # pip install python-dateutil
    
    
    class JSONEncoder(json.JSONEncoder):
    
        def default(self, obj):
            if isinstance(obj, datetime.datetime):
                return obj.isoformat()
            return super(JSONEncoder, self).default(obj)
    
    
    def test():
        dts = [
            datetime.datetime.now(),
            datetime.datetime.now(datetime.timezone(-datetime.timedelta(hours=4))),
            datetime.datetime.utcnow(),
            datetime.datetime.now(datetime.timezone.utc),
        ]
        for dt in dts:
            dt_isoformat = json.loads(json.dumps(dt, cls=JSONEncoder))
            dt_parsed = dateutil.parser.parse(dt_isoformat)
            assert dt == dt_parsed
            print(f'{dt}, {dt_isoformat}, {dt_parsed}')
            # 2018-07-22 02:22:42.910637, 2018-07-22T02:22:42.910637, 2018-07-22 02:22:42.910637
            # 2018-07-22 02:22:42.910643-04:00, 2018-07-22T02:22:42.910643-04:00, 2018-07-22 02:22:42.910643-04:00
            # 2018-07-22 06:22:42.910645, 2018-07-22T06:22:42.910645, 2018-07-22 06:22:42.910645
            # 2018-07-22 06:22:42.910646+00:00, 2018-07-22T06:22:42.910646+00:00, 2018-07-22 06:22:42.910646+00:00
    
    
    if __name__ == '__main__':
        test()
    
    0 讨论(0)
  • 2020-11-22 03:45

    Updated for 2018

    The original answer accommodated the way MongoDB "date" fields were represented as:

    {"$date": 1506816000000}

    If you want a generic Python solution for serializing datetime to json, check out @jjmontes' answer for a quick solution which requires no dependencies.


    As you are using mongoengine (per comments) and pymongo is a dependency, pymongo has built-in utilities to help with json serialization:
    http://api.mongodb.org/python/1.10.1/api/bson/json_util.html

    Example usage (serialization):

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

    Example usage (deserialization):

    json.loads(aJsonString, object_hook=json_util.object_hook)
    

    Django

    Django provides a native DjangoJSONEncoder serializer that deals with this kind of properly.

    See https://docs.djangoproject.com/en/dev/topics/serialization/#djangojsonencoder

    from django.core.serializers.json import DjangoJSONEncoder
    
    return json.dumps(
      item,
      sort_keys=True,
      indent=1,
      cls=DjangoJSONEncoder
    )
    

    One difference I've noticed between DjangoJSONEncoder and using a custom default like this:

    import datetime
    import json
    
    def default(o):
        if isinstance(o, (datetime.date, datetime.datetime)):
            return o.isoformat()
    
    return json.dumps(
      item,
      sort_keys=True,
      indent=1,
      default=default
    )
    

    Is that Django strips a bit of the data:

     "last_login": "2018-08-03T10:51:42.990", # DjangoJSONEncoder 
     "last_login": "2018-08-03T10:51:42.990239", # default
    

    So, you may need to be careful about that in some cases.

    0 讨论(0)
  • 2020-11-22 03:45

    I have an application with a similar issue; my approach was to JSONize the datetime value as a 6-item list (year, month, day, hour, minutes, seconds); you could go to microseconds as a 7-item list, but I had no need to:

    class DateTimeEncoder(json.JSONEncoder):
        def default(self, obj):
            if isinstance(obj, datetime.datetime):
                encoded_object = list(obj.timetuple())[0:6]
            else:
                encoded_object =json.JSONEncoder.default(self, obj)
            return encoded_object
    
    sample = {}
    sample['title'] = "String"
    sample['somedate'] = datetime.datetime.now()
    
    print sample
    print json.dumps(sample, cls=DateTimeEncoder)
    

    produces:

    {'somedate': datetime.datetime(2013, 8, 1, 16, 22, 45, 890000), 'title': 'String'}
    {"somedate": [2013, 8, 1, 16, 22, 45], "title": "String"}
    
    0 讨论(0)
  • 2020-11-22 03:45

    I had encountered same problem when externalizing django model object to dump as JSON. Here is how you can solve it.

    def externalize(model_obj):
      keys = model_obj._meta.get_all_field_names() 
      data = {}
      for key in keys:
        if key == 'date_time':
          date_time_obj = getattr(model_obj, key)
          data[key] = date_time_obj.strftime("%A %d. %B %Y")
        else:
          data[key] = getattr(model_obj, key)
      return data
    
    0 讨论(0)
  • 2020-11-22 03:46

    My solution (with less verbosity, I think):

    def default(o):
        if type(o) is datetime.date or type(o) is datetime.datetime:
            return o.isoformat()
    
    def jsondumps(o):
        return json.dumps(o, default=default)
    

    Then use jsondumps instead of json.dumps. It will print:

    >>> jsondumps({'today': datetime.date.today()})
    '{"today": "2013-07-30"}'
    

    I you want, later you can add other special cases to this with a simple twist of the default method. Example:

    def default(o):
        if type(o) is datetime.date or type(o) is datetime.datetime:
            return o.isoformat()
        if type(o) is decimal.Decimal:
            return float(o)
    
    0 讨论(0)
  • 2020-11-22 03:47

    Here is my solution:

    import json
    
    
    class DatetimeEncoder(json.JSONEncoder):
        def default(self, obj):
            try:
                return super().default(obj)
            except TypeError:
                return str(obj)
    

    Then you can use it like that:

    json.dumps(dictionnary, cls=DatetimeEncoder)
    
    0 讨论(0)
提交回复
热议问题