当前位置: 首页 > 知识库问答 >
问题:

支持日期时间的Python JSON编码器?

葛航
2023-03-14

有没有什么优雅的方法使Python JSON编码器支持datetime?一些第三方模块或简易黑客?

我正在使用tornado的数据库包装器从db获取一些行来生成一个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代码本身,也不想修改Tornado源代码。我是否可以用自己的项目代码来实现这一点?最好是一步走。

非常感谢!

共有3个答案

东门清夷
2023-03-14

我为我的项目制作了自己的课程:

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文档
  • Mark Hildreth--子类JSONEncoder和JSONDecoder
  • Cédric Krier--trytond.protocols.jsonrpc源代码

注意:通过将类型作为键和参数、KWARG作为值的自定义字典传递给编码器的\uuuu init\uuuu()并在default()方法中使用该字典(或默认字典),可以使其更加灵活。

岳枫
2023-03-14

文档建议将JSONEncoder子类化并实现您自己的默认方法。看起来你基本上就在那里,这不是一个“肮脏的黑客”。

默认编码器不处理日期的原因是JSON中没有日期的标准表示形式。有些人使用的是/Date(1198908717056)/格式,但我个人更喜欢ISO格式。

import json
import datetime


class DateTimeEncoder(json.JSONEncoder):

    def default(self, obj):
        if isinstance(obj, (datetime.datetime, datetime.date, datetime.time)):
            return obj.isoformat()
        elif isinstance(obj, datetime.timedelta):
            return (datetime.datetime.min + obj).time().isoformat()

        return super(DateTimeEncoder, self).default(obj)

now = datetime.datetime.now()
encoder = DateTimeEncoder()
encoder.encode({"datetime": now, "date": now.date(), "time": now.time()})
> {"datetime": "2019-07-02T16:17:09.990126", "date": "2019-07-02", "time": "16:17:09.990126"}
法浩壤
2023-03-14

json。转储(thing,default=str)

 类似资料:
  • 问题内容: 有什么优雅的方法可以使Python JSON编码器支持日期时间?一些第三者模块还是容易破解? 我正在使用tornado的数据库包装程序从db获取一些行以生成json。查询结果包括一个常规的MySQL时间戳列。 令人讨厌的是,Python的默认json编码器不支持自己的日期时间类型,这种类型在所有类型的数据库查询中都很常见。 我不想修改Python自己的json编码器。有什么好的做法吗?

  • 问题内容: 我将Java 8用于新项目。 我正在尝试在Java 8中使用新的日期和时间api,但是我不知道是否JPA 2.1完全支持此新的日期和时间API。 请分享您在JPA对Java 8中新日期和时间API的支持中的经验/意见。 我可以通过JPA 2.1安全地在Java 8中使用新的日期和时间api吗? 更新: 我正在使用Hibernate(4.3.5.Final)作为JPA实现。 问题答案:

  • 我需要一些解释为什么这个代码不编译: 错误: 类型Duration中的(TemporalAmount)方法不适用于参数(ChronoUnit) 正如本文所述: public static Duration from(TemporalAmount amount)从时间量中获取持续时间的实例。这将根据指定的金额获得持续时间。TemporalAmount表示一个时间量,可以是基于日期的,也可以是基于时间

  • SRS哪些协议支持hevc编码 支持hevc编码的协议: rtmp httpflv hls srt: 具体见srt codec wiki FLV视频头信息 因为rtmp在2012年后,协议没有更新,对hevc编码格式的支持在rtmp协议官方文档中没有明确定义。 国内cdn厂商通过修改<video>中CodecID的定义,将flv中的hevc codecid定义为12。 Field Type Com

  • 问题内容: 我有一个脚本,需要在脚本的不同行执行以下命令: 在我的陈述中,我有以下内容: 我收到以下错误: 如果我将语句的顺序更改为: 我收到以下错误: 如果我再次将语句更改为: 我收到以下错误: 这是怎么回事,我怎么都可以工作? 问题答案: 您的麻烦是,您有一些代码希望对 模块 进行引用,而其他代码希望对类进行引用 。 显然,不能两者兼有。 当您这样做时: 您首先要设置为对该类的引用,然后立即将