Python JSON encoder to support datetime? Python JSON encoder to support datetime? python python

Python JSON encoder to support datetime?


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 jsonimport datetimeclass 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"}


I made my own classes for my project:

import datetimeimport decimalimport jsonimport sysclass 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:

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.