Python的JSON编码器支持日期时间?(Python JSON encoder to suppo

2019-06-27 08:45发布

是否有任何优雅的方式来使Python JSON编码器支持日期时间? 一些第三方的模块或容易破解?

我使用的龙卷风的数据库包装器从数据库获取一些原糖生成一个JSON。 查询结果包括普通的MySQL时间戳列。

这是相当烦人,Python的默认编码器的JSON不支持自己的时间类型,这是在各种数据库查询的那么普遍。

我不想修改Python的JSON编码器。 任何好的做法? 非常感谢!

PS:我发现了一个肮脏的黑客通过修改的Python JSON编码器默认方法:

更改:

def default(self, o):
    raise TypeError(repr(o) + " is not JSON serializable")

至:

def default(self, o):
    from datetime import date
    from datetime import datetime
    if isinstance(o, datetime):
        return o.isoformat()
    elif isinstance(o, date):
        return o.isoformat()
    else:
        raise TypeError(repr(o) + " is not JSON serializable")

好了,这将是只为开发环境治标不治本。

但对于长期的解决办法或生产环境中,这是相当丑陋,我有我每次部署新服务器的时间做了修改。

有没有更好的办法? 我不想修改Python代码本身,既不是龙卷风的源代码。 有什么我可以用我自己的项目代码做才能让这种情况发生? 优选在一个步伐。

非常感谢!

Answer 1:

该文档建议继承JSONEncoder和实现自己的默认方法。 好像你基本上没有,这不是一个“肮脏的黑客”。

之所以日期不会被默认编码器处理的是有一个JSON的日期没有标准表示。 有些人正在使用的格式/Date(1198908717056)/ ,但我更喜欢ISO格式的个人。

import datetime

class DateTimeEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, datetime.datetime):
            return obj.isoformat()
        elif isinstance(obj, datetime.date):
            return obj.isoformat()
        elif isinstance(obj, datetime.timedelta):
            return (datetime.datetime.min + obj).time().isoformat()
        else:
            return super(DateTimeEncoder, self).default(obj)

DateTimeEncoder().encode(object)


Answer 2:

json.dumps(thing, default=str)



Answer 3:

我做了我自己的类为我的项目:

import datetime
import decimal
import json
import sys

class EnhancedJSONEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, datetime.datetime):
            ARGS = ('year', 'month', 'day', 'hour', 'minute',
                     'second', 'microsecond')
            return {'__type__': 'datetime.datetime',
                    'args': [getattr(obj, a) for a in ARGS]}
        elif isinstance(obj, datetime.date):
            ARGS = ('year', 'month', 'day')
            return {'__type__': 'datetime.date',
                    'args': [getattr(obj, a) for a in ARGS]}
        elif isinstance(obj, datetime.time):
            ARGS = ('hour', 'minute', 'second', 'microsecond')
            return {'__type__': 'datetime.time',
                    'args': [getattr(obj, a) for a in ARGS]}
        elif isinstance(obj, datetime.timedelta):
            ARGS = ('days', 'seconds', 'microseconds')
            return {'__type__': 'datetime.timedelta',
                    'args': [getattr(obj, a) for a in ARGS]}
        elif isinstance(obj, decimal.Decimal):
            return {'__type__': 'decimal.Decimal',
                    'args': [str(obj),]}
        else:
            return super().default(obj)


class EnhancedJSONDecoder(json.JSONDecoder):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, object_hook=self.object_hook,
                         **kwargs)

    def object_hook(self, d): 
        if '__type__' not in d:
            return d
        o = sys.modules[__name__]
        for e in d['__type__'].split('.'):
            o = getattr(o, e)
        args, kwargs = d.get('args', ()), d.get('kwargs', {})
        return o(*args, **kwargs)

if __name__ == '__main__':
    j1 = json.dumps({'now': datetime.datetime.now(),
        'val': decimal.Decimal('9.3456789098765434987654567')},
        cls=EnhancedJSONEncoder)
    print(j1)
    o1 = json.loads(j1, cls=EnhancedJSONDecoder)
    print(o1)

结果:

{"val": {"args": ["9.3456789098765434987654567"], "__type__": "decimal.Decimal"}, "now": {"args": [2014, 4, 29, 11, 44, 57, 971600], "__type__": "datetime.datetime"}}
{'val': Decimal('9.3456789098765434987654567'), 'now': datetime.datetime(2014, 4, 29, 11, 44, 57, 971600)}

参考文献:

  • JSON文件
  • 马克·希尔德里斯-子类JSONEncoder和JSONDecoder
  • 塞德里克克瑞尔- trytond.protocols.jsonrpc源代码

注意:它可以更加灵活,通过将一个自定义字典,类型为按键和args,kwargs因为值编码器的__init__()和使用(或默认字典)的default()方法。



Answer 4:

json.dumps(r, default=lambda o: o.isoformat() if hasattr(o, 'isoformat') else o)


Answer 5:

该Tryton项目具有JSONEncoder实施datetime.datetimedatetime.datedatetime.time对象(与别人)。 它用于服务器和客户端之间的JSON RPC通信。

见http://hg.tryton.org/2.4/trytond/file/ade5432ac476/trytond/protocols/jsonrpc.py#l53



Answer 6:

转换日期时间类型为Unix时间戳,然后编码所述内容成JSON。

例如: http://codepad.org/k3qF09Kr



Answer 7:

只要创建一个自定义编码器

(小而重要的除了科尔的回答是pd.NaT(或空/空的时间戳值)的处理,因为不加你会得到非常奇怪的时间戳转换为NAT /缺少时间戳数据)

class CustomEncoder(json.JSONEncoder):
    def default(self, obj):
        if pd.isnull(obj):
            return None
        elif isinstance(obj, datetime):
            return obj.isoformat()
        elif isinstance(obj, date):
            return obj.isoformat()
        elif isinstance(obj, timedelta):
            return (datetime.min + obj).time().isoformat()
        else:
            return super(CustomEncoder, self).default(obj)

然后用它来编码数据帧:

df_as_dict = df.to_dict(outtype = 'records')  # transform to dict

df_as_json = CustomEncoder().encode(df_as_dict) #transform to json

由于编码器标准化的数据,常规解码器将采取行动以转变回一个数据帧罚款:

result_as_dict = json.JSONDecoder().decode(df_as_json) # decode back to dict

result_df = pd.DataFrame(result)  # transform dict back to dataframe

当然,如果你把数据帧到一个更大的字典编码之前,比如,这也将工作

input_dict = {'key_1':val_1,'key_2':val_2,...,'df_as_dict':df_as_dict}
input_json = CustomEncoder().encode(input_dict)
input_json_back_as_dict = json.JSONDecoder().decode(input_json)
input_df_back_as_dict = input_json_back_as_dict['df_as_dict']
input_df_back_as_df = pd.DataFrame(input_df_back_as_dict)


文章来源: Python JSON encoder to support datetime?