Python JSON encoder to support datetime?

后端 未结 9 1884
长发绾君心
长发绾君心 2020-12-03 10:24

is there any elegant way to make Python JSON encoder support datetime? some 3rd party module or easy hack?

I am using tornado\'s database wrapper to fetch some rows

相关标签:
9条回答
  • 2020-12-03 10:40

    I made my own classes for my project:

    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)
    

    Result:

    {"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)}
    

    References:

    • json Documentation
    • Mark Hildreth -- Subclassing JSONEncoder and JSONDecoder
    • Cédric Krier -- trytond.protocols.jsonrpc source code

    Note: It can be made more flexible by passing a custom dictionary with types as keys and args, kwargs as values to the encoder's __init__() and use that (or a default dictionary) in the default() method.

    0 讨论(0)
  • 2020-12-03 10:47
    json.dumps(r, default=lambda o: o.isoformat() if hasattr(o, 'isoformat') else o)
    
    0 讨论(0)
  • 2020-12-03 10:48

    The docs suggest subclassing JSONEncoder and implementing your own default method. Seems like you're basically there, and it's not a "dirty hack".

    The reason dates aren't handled by the default encoder is there is no standard representation of a date in JSON. Some people are using the format /Date(1198908717056)/, but I prefer ISO format personally.

    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"}
    
    0 讨论(0)
  • 2020-12-03 10:49

    json.dumps(thing, default=str)

    0 讨论(0)
  • 2020-12-03 10:52

    I recommend to use the ujson package or the orjson one.

    They are much faster and still support several complex types.

    0 讨论(0)
  • 2020-12-03 10:54

    The Tryton project has a JSONEncoder implementation for datetime.datetime, datetime.date and datetime.time objects (with others). It is used for JSON RPC communication between the server and client.

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

    0 讨论(0)
提交回复
热议问题