首页 文章

如何克服“datetime.datetime而不是JSON可序列化”?

提问于
浏览
476

我有一个基本的词典如下:

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

当我尝试做 jsonify(sample) 时,我得到:

TypeError: datetime.datetime(2012, 8, 8, 21, 46, 24, 862000) is not JSON serializable

我能做些什么,以便我的字典样本可以克服上述错误?

注意:虽然它可能不相关,但字典是从mongodb中检索记录生成的,当我打印出 str(sample['somedate']) 时,输出为 2012-08-08 21:46:24.862000 .

29 回答

  • 9

    在使用sqlalchemy在类中编写序列化装饰器时,我收到了相同的错误消息 . 所以代替:

    Class Puppy(Base):
        ...
        @property
        def serialize(self):
            return { 'id':self.id,
                     'date_birth':self.date_birth,
                      ...
                    }
    

    我简单地借用了jgbarah的使用isoformat()并将原始值附加到isoformat()的想法,现在它看起来像:

    ...
                     'date_birth':self.date_birth.isoformat(),
                      ...
    
  • 1

    如果您想要自己的格式,请快速修复

    for key,val in sample.items():
        if isinstance(val, datetime):
            sample[key] = '{:%Y-%m-%d %H:%M:%S}'.format(val) #you can add different formating here
    json.dumps(sample)
    
  • 15

    更新于2018年

    最初的答案符合MongoDB“date”字段的表示方式:

    {"$date": 1506816000000}

    如果你想要一个通用的Python解决方案来序列化 datetime 到json,请查看@jjmontes' answer以获得一个不需要依赖的快速解决方案 .


    当您使用mongoengine(每条评论)并且pymongo是一个依赖项时,pymongo有内置的实用程序来帮助json序列化:
    http://api.mongodb.org/python/1.10.1/api/bson/json_util.html

    用法示例(序列化):

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

    用法示例(反序列化):

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

    Django

    Django提供了一个原生的 DjangoJSONEncoder 序列化器来处理这种正确的 .

    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
    )
    

    我注意到 DjangoJSONEncoder 与使用自定义_1856348之间的一个区别是:

    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
    )
    

    这是Django剥离了一些数据:

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

    因此,在某些情况下,您可能需要小心 .

  • 2

    基于其他答案,基于特定序列化器的简单解决方案,只需将 datetime.datetimedatetime.date 对象转换为字符串 .

    from datetime import date, datetime
    
    def json_serial(obj):
        """JSON serializer for objects not serializable by default json code"""
    
        if isinstance(obj, (datetime, date)):
            return obj.isoformat()
        raise TypeError ("Type %s not serializable" % type(obj))
    

    如图所示,代码只是检查以确定对象是否为 datetime.datetimedatetime.date 类,然后使用 .isoformat() 生成它的序列化版本,根据ISO 8601格式,YYYY-MM-DDTHH:MM:SS(很容易由JavaScript解码) . 如果寻求更复杂的序列化表示,可以使用其他代码而不是str()(例如,参见该问题的其他答案) . 代码以引发异常结束,以处理使用非可序列化类型调用的情况 .

    这个json_serial函数可以用如下:

    from datetime import datetime
    from json import dumps
    
    print dumps(datetime.now(), default=json_serial)
    

    有关如何使用json.dumps的默认参数的详细信息,请参见Section Basic Usage of the json module documentation .

  • 0

    我的快速和脏的JSON转储吃日期和所有东西:

    json.dumps(my_dictionary, indent=4, sort_keys=True, default=str)
    
  • 7

    我刚遇到这个问题,我的解决方案是子类 json.JSONEncoder

    from datetime import datetime
    import json
    
    class DateTimeEncoder(json.JSONEncoder):
        def default(self, o):
            if isinstance(o, datetime):
                return o.isoformat()
    
            return json.JSONEncoder.default(self, o)
    

    在你的电话中做了类似的事情: json.dumps(yourobj, cls=DateTimeEncoder) .isoformat() 我从上面的一个答案得到了 .

  • 76

    将日期转换为字符串

    sample['somedate'] = str( datetime.utcnow() )
    
  • 20

    对于那些不需要或不想使用pymongo库的人来说,你可以使用这个小片段轻松实现datetime JSON转换:

    def default(obj):
        """Default JSON serializer."""
        import calendar, datetime
    
        if isinstance(obj, datetime.datetime):
            if obj.utcoffset() is not None:
                obj = obj - obj.utcoffset()
            millis = int(
                calendar.timegm(obj.timetuple()) * 1000 +
                obj.microsecond / 1000
            )
            return millis
        raise TypeError('Not sure how to serialize %s' % (obj,))
    

    然后像这样使用它:

    import datetime, json
    print json.dumps(datetime.datetime.now(), default=default)
    

    输出:

    '1365091796124'
    
  • 164

    这是我的解决方案:

    # -*- coding: utf-8 -*-
    import json
    
    
    class DatetimeEncoder(json.JSONEncoder):
        def default(self, obj):
            try:
                return super(DatetimeEncoder, obj).default(obj)
            except TypeError:
                return str(obj)
    

    然后你就可以这样使用它:

    json.dumps(dictionnary, cls=DatetimeEncoder)
    
  • 3

    我有一个类似问题的申请;我的方法是将日期时间值JSONize为6项列表(年,月,日,小时,分钟,秒);你可以将微秒作为一个7项目列表,但我没有必要:

    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)
    

    生产环境 :

    {'somedate': datetime.datetime(2013, 8, 1, 16, 22, 45, 890000), 'title': 'String'}
    {"somedate": [2013, 8, 1, 16, 22, 45], "title": "String"}
    
  • 30

    我的解决方案(我认为不那么冗长):

    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)
    

    然后使用 jsondumps 而不是 json.dumps . 它将打印:

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

    我想要,稍后你可以通过简单的 default 方法添加其他特殊情况 . 例:

    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)
    
  • 249

    这个Q一次又一次地重复 - 一种修补json模块的简单方法,使序列化支持日期时间 .

    import json
    import datetime
    
    json.JSONEncoder.default = lambda self,obj: (obj.isoformat() if isinstance(obj, datetime.datetime) else None)
    

    比你经常使用json序列化 - 这次将datetime序列化为isoformat .

    json.dumps({'created':datetime.datetime.now()})
    

    导致:'{“created”:“2015-08-26T14:21:31.853855”}'

    查看更多详情和一些警告:StackOverflow: JSON datetime between Python and JavaScript

  • 2

    这是一个简单的解决方案来解决“datetime not JSON serializable”问题 .

    enco = lambda obj: (
        obj.isoformat()
        if isinstance(obj, datetime.datetime)
        or isinstance(obj, datetime.date)
        else None
    )
    
    json.dumps({'date': datetime.datetime.now()}, default=enco)
    

    Output:-> {"date":"2015-12-16T04:48:20.024609"}

  • 2

    您必须提供 json.dumps 参数为 json.dumps 的自定义编码器类 . 引用docs

    >>> import json
    >>> class ComplexEncoder(json.JSONEncoder):
    ...     def default(self, obj):
    ...         if isinstance(obj, complex):
    ...             return [obj.real, obj.imag]
    ...         return json.JSONEncoder.default(self, obj)
    ...
    >>> dumps(2 + 1j, cls=ComplexEncoder)
    '[2.0, 1.0]'
    >>> ComplexEncoder().encode(2 + 1j)
    '[2.0, 1.0]'
    >>> list(ComplexEncoder().iterencode(2 + 1j))
    ['[', '2.0', ', ', '1.0', ']']
    

    这使用复数作为示例,但您可以轻松创建一个类来编码日期(除了我认为JSON对日期有点模糊)

  • 110

    最简单的方法是将日期时间格式的dict部分更改为isoformat . 该值实际上是isoformat中的一个字符串,json可以使用 .

    v_dict = version.dict()
    v_dict['created_at'] = v_dict['created_at'].isoformat()
    
  • 365

    您应该在 .datetime.now() 方法上使用 .strftime() 方法将其作为可序列化方法 .

    这是一个例子:

    from datetime import datetime
    
    time_dict = {'time': datetime.now().strftime('%Y-%m-%dT%H:%M:%S')}
    sample_dict = {'a': 1, 'b': 2}
    sample_dict.update(time_dict)
    sample_dict
    

    输出:

    Out[0]: {'a': 1, 'b': 2, 'time': '2017-10-31T15:16:30'}
    
  • 2

    如果你是在视图中使用结果一定要返回正确的响应 . 根据API,jsonify执行以下操作:

    使用application / json mimetype创建具有给定参数的JSON表示的Response .

    要使用json.dumps模仿此行为,您必须添加一些额外的代码行 .

    response = make_response(dumps(sample, cls=CustomEncoder))
    response.headers['Content-Type'] = 'application/json'
    response.headers['mimetype'] = 'application/json'
    return response
    

    你还应该返回一个dict来完全复制jsonify的响应 . 因此,整个文件将如下所示

    from flask import make_response
    from json import JSONEncoder, dumps
    
    
    class CustomEncoder(JSONEncoder):
        def default(self, obj):
            if set(['quantize', 'year']).intersection(dir(obj)):
                return str(obj)
            elif hasattr(obj, 'next'):
                return list(obj)
            return JSONEncoder.default(self, obj)
    
    @app.route('/get_reps/', methods=['GET'])
    def get_reps():
        sample = ['some text', <datetime object>, 123]
        response = make_response(dumps({'result': sample}, cls=CustomEncoder))
        response.headers['Content-Type'] = 'application/json'
        response.headers['mimetype'] = 'application/json'
        return response
    
  • 1

    我的解决方案

    from datetime import datetime
    import json
    
    from pytz import timezone
    import pytz
    
    
    def json_dt_serializer(obj):
        """JSON serializer, by macm.
        """
        rsp = dict()
        if isinstance(obj, datetime):
            rsp['day'] = obj.day
            rsp['hour'] = obj.hour
            rsp['microsecond'] = obj.microsecond
            rsp['minute'] = obj.minute
            rsp['month'] = obj.month
            rsp['second'] = obj.second
            rsp['year'] = obj.year
            rsp['tzinfo'] = str(obj.tzinfo)
            return rsp
        raise TypeError("Type not serializable")
    
    
    def json_dt_deserialize(obj):
        """JSON deserialize from json_dt_serializer, by macm.
        """
        if isinstance(obj, str):
            obj = json.loads(obj)
        tzone = timezone(obj['tzinfo'])
        tmp_dt = datetime(obj['year'],
                          obj['month'],
                          obj['day'],
                          hour=obj['hour'],
                          minute=obj['minute'],
                          second=obj['second'],
                          microsecond=obj['microsecond'])
        loc_dt = tzone.localize(tmp_dt)
        deserialize = loc_dt.astimezone(tzone)
        return deserialize
    

    好的,现在进行一些测试 .

    # Tests
    now = datetime.now(pytz.utc)
    
    # Using this solution
    rsp = json_dt_serializer(now)
    tmp = json_dt_deserialize(rsp)
    assert tmp == now
    assert isinstance(tmp, datetime) == True
    assert isinstance(now, datetime) == True
    
    # using default from json.dumps
    tmp = json.dumps(datetime.now(pytz.utc), default=json_dt_serializer)
    rsp = json_dt_deserialize(tmp)
    assert isinstance(rsp, datetime) == True
    
    # Lets try another timezone
    eastern = timezone('US/Eastern')
    now = datetime.now(eastern)
    rsp = json_dt_serializer(now)
    tmp = json_dt_deserialize(rsp)
    
    print(tmp)
    # 2015-10-22 09:18:33.169302-04:00
    
    print(now)
    # 2015-10-22 09:18:33.169302-04:00
    
    # Wow, Works!
    assert tmp == now
    
  • -1

    这是我将datetime转换为JSON并返回的完整解决方案..

    import calendar, datetime, json
    
    def outputJSON(obj):
        """Default JSON serializer."""
    
        if isinstance(obj, datetime.datetime):
            if obj.utcoffset() is not None:
                obj = obj - obj.utcoffset()
    
            return obj.strftime('%Y-%m-%d %H:%M:%S.%f')
        return str(obj)
    
    def inputJSON(obj):
        newDic = {}
    
        for key in obj:
            try:
                if float(key) == int(float(key)):
                    newKey = int(key)
                else:
                    newKey = float(key)
    
                newDic[newKey] = obj[key]
                continue
            except ValueError:
                pass
    
            try:
                newDic[str(key)] = datetime.datetime.strptime(obj[key], '%Y-%m-%d %H:%M:%S.%f')
                continue
            except TypeError:
                pass
    
            newDic[str(key)] = obj[key]
    
        return newDic
    
    x = {'Date': datetime.datetime.utcnow(), 34: 89.9, 12.3: 90, 45: 67, 'Extra': 6}
    
    print x
    
    with open('my_dict.json', 'w') as fp:
        json.dump(x, fp, default=outputJSON)
    
    with open('my_dict.json') as f:
        my_dict = json.load(f, object_hook=inputJSON)
    
    print my_dict
    

    产量

    {'Date': datetime.datetime(2013, 11, 8, 2, 30, 56, 479727), 34: 89.9, 45: 67, 12.3: 90, 'Extra': 6}
    {'Date': datetime.datetime(2013, 11, 8, 2, 30, 56, 479727), 34: 89.9, 45: 67, 12.3: 90, 'Extra': 6}
    

    JSON文件

    {"Date": "2013-11-08 02:30:56.479727", "34": 89.9, "45": 67, "12.3": 90, "Extra": 6}
    

    这使我能够导入和导出字符串,整数,浮点数和日期时间对象 . 它不应该难以扩展到其他类型 .

  • 5

    date 转换为 string

    date = str(datetime.datetime(somedatetimehere))
    
  • 5

    通常,有几种方法可以序列化日期时间,例如:

    • ISO字符串,简短并且可以包括时区信息,例如@ jgbarah的answer

    • 时间戳(时区数据丢失),例如@ JayTaylor的answer

    • 属性词典(包括时区) .

    如果您对最后一种方式没问题,json_tricks包处理日期,时间和日期时间,包括时区 .

    from datetime import datetime
    from json_tricks import dumps
    foo = {'title': 'String', 'datetime': datetime(2012, 8, 8, 21, 46, 24, 862000)}
    dumps(foo)
    

    这使:

    {"title": "String", "datetime": {"__datetime__": null, "year": 2012, "month": 8, "day": 8, "hour": 21, "minute": 46, "second": 24, "microsecond": 862000}}
    

    所以你需要做的就是

    `pip install json_tricks`
    

    然后从 json_tricks 导入而不是 json .

    解码时不会将其作为单个字符串,int或float存储的优点:如果您只遇到一个字符串或特别是int或float,您需要了解一些有关数据的信息,以了解它是否为日期时间 . 作为一个字典,您可以存储元数据,以便自动解码,这就是 json_tricks 为您所做的 . 它也很容易为人类编辑 .

    免责声明:它是由我制作的 . 因为我有同样的问题 .

  • 294

    试试这个用一个例子来解析它:

    #!/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()
    
  • 2

    json.dumps方法可以接受一个名为default的可选参数,该参数应该是一个函数 . 每次JSON尝试转换值时,它都不知道如何转换它将调用我们传递给它的函数 . 该函数将接收有问题的对象,并期望返回该对象的JSON表示 .

    def myconverter(o):
     if isinstance(o, datetime.datetime):
        return o.__str__()
    
    print(json.dumps(d, default = myconverter))
    
  • 1

    如果您使用的是python3.7,那么最好的解决方案是使用datetime.isoformat()datetime.fromisoformat();他们使用天真和有意识的 datetime 对象:

    #!/usr/bin/env python3.7
    
    from datetime import datetime
    from datetime import timezone
    from datetime import timedelta
    import json
    
    def default(obj):
        if isinstance(obj, datetime):
            return { '_isoformat': obj.isoformat() }
        return super().default(obj)
    
    def object_hook(obj):
        _isoformat = obj.get('_isoformat')
        if _isoformat is not None:
            return datetime.fromisoformat(_isoformat)
        return obj
    
    if __name__ == '__main__':
        #d = { 'now': datetime(2000, 1, 1) }
        d = { 'now': datetime(2000, 1, 1, tzinfo=timezone(timedelta(hours=-8))) }
        s = json.dumps(d, default=default)
        print(s)
        print(d == json.loads(s, object_hook=object_hook))
    

    输出:

    {"now": {"_isoformat": "2000-01-01T00:00:00-08:00"}}
    True
    

    如果您使用的是python3.6或更低版本,而您只关心时间值(而不是时区),那么您可以使用 datetime.timestamp()datetime.fromtimestamp() 代替;

    如果您正在使用python3.6或更低版本,并且您确实关心时区,那么您可以通过 datetime.tzinfo 获取它,但您必须自己序列化此字段;最简单的方法是在序列化对象中添加另一个字段 _tzinfo ;

    最后,要注意所有这些例子中的精确性;

  • 0

    如果您在通信的两侧,可以使用 repr()eval() 函数以及json .

    import datetime, json
    
    dt = datetime.datetime.now()
    print("This is now: {}".format(dt))
    
    dt1 = json.dumps(repr(dt))
    print("This is serialised: {}".format(dt1))
    
    dt2 = json.loads(dt1)
    print("This is loaded back from json: {}".format(dt2))
    
    dt3 = eval(dt2)
    print("This is the same object as we started: {}".format(dt3))
    
    print("Check if they are equal: {}".format(dt == dt3))
    

    您不应该将日期时间导入为

    from datetime import datetime
    

    因为eval会抱怨 . 或者您可以将datetime作为参数传递给eval . 无论如何,这应该有效 .

  • 3

    当将django模型对象外部化为转储为JSON时,我遇到了同样的问题 . 这是你如何解决它 .

    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
    def j_serial(o):     # self contained
        from datetime import datetime, date
        return str(o).split('.')[0] if isinstance(o, (datetime, date)) else None
    

    上述用途的用法:

    import datetime
    serial_d = j_serial(datetime.datetime.now())
    if serial_d:
        print(serial_d)  # output: 2018-02-28 02:23:15
    
  • 1

    实际上这很简单 . 如果您需要经常序列化日期,那么将它们作为字符串使用 . 如果需要,您可以轻松地将它们转换为datetime对象 .

    如果您需要主要作为datetime对象工作,那么在序列化之前将它们转换为字符串 .

    import json, datetime
    
    date = str(datetime.datetime.now())
    print(json.dumps(date))
    "2018-12-01 15:44:34.409085"
    print(type(date))
    <class 'str'>
    
    datetime_obj = datetime.datetime.strptime(date, '%Y-%m-%d %H:%M:%S.%f')
    print(datetime_obj)
    2018-12-01 15:44:34.409085
    print(type(datetime_obj))
    <class 'datetime.datetime'>
    

    如您所见,两种情况下的输出都相同 . 只有类型不同 .

  • 15

    我可能不是100%正确但是,这是进行序列化的简单方法

    #!/usr/bin/python
    import datetime,json
    
    sampledict = {}
    sampledict['a'] = "some string"
    sampledict['b'] = datetime.datetime.now()
    
    print sampledict   # output : {'a': 'some string', 'b': datetime.datetime(2017, 4, 15, 5, 15, 34, 652996)}
    
    #print json.dumps(sampledict)
    
    '''
    output : 
    
    Traceback (most recent call last):
      File "./jsonencodedecode.py", line 10, in <module>
        print json.dumps(sampledict)
      File "/usr/lib/python2.7/json/__init__.py", line 244, in dumps
        return _default_encoder.encode(obj)
      File "/usr/lib/python2.7/json/encoder.py", line 207, in encode
        chunks = self.iterencode(o, _one_shot=True)
      File "/usr/lib/python2.7/json/encoder.py", line 270, in iterencode
        return _iterencode(o, 0)
      File "/usr/lib/python2.7/json/encoder.py", line 184, in default
        raise TypeError(repr(o) + " is not JSON serializable")
    TypeError: datetime.datetime(2017, 4, 15, 5, 16, 17, 435706) is not JSON serializable
    
    
    '''
    
    sampledict['b'] = datetime.datetime.now().strftime("%B %d, %Y %H:%M %p")
    
    afterdump = json.dumps(sampledict)
    
    print afterdump  #output : {"a": "some string", "b": "April 15, 2017 05:18 AM"}
    
    print type(afterdump) #<type 'str'>
    
    
    afterloads = json.loads(afterdump) 
    
    print afterloads # output : {u'a': u'some string', u'b': u'April 15, 2017 05:18 AM'}
    
    
    print type(afterloads) # output :<type 'dict'>
    

相关问题