json.dump not calling default or cls
TLDR: .default
is only called for dict
values, not keys.
To elaborate on Abhishek Kumars answer; after reviewing the json.JSONEncoder
source, .default
is only ever called on dict
values that are not serializable, never keys. Keys must be one of str
, int
, float
, bool
or None
.
To use datetime
objects as keys, .iterencode
must be overwritten to use recursive pre-processor method to convert datetime
to str
:
test = { "key_1": "Value_1", "key_2": 10, "key_3": ["list_" + str(i) for i in range(5)], "key_4": {"nestkey_" + str(i) : "nestvalue_" + str(i) for i in range(5) }, "key_5": datetime.datetime(1970, 1, 1, 8, 0, 0), datetime.datetime(1970, 1, 1, 8, 0, 0): "datetime_key"}class DateTimeEncoder(json.JSONEncoder): def _preprocess_date(self, obj): if isinstance(obj, (datetime.date, datetime.datetime, datetime.timedelta)): return str(obj) elif isinstance(obj, dict): return {self._preprocess_date(k): self._preprocess_date(v) for k,v in obj.items()} elif isinstance(obj, list): return [self._preprocess_date(i) for i in obj] return obj def default(self, obj): if isinstance(obj, (datetime.date, datetime.datetime, datetime.timedelta)): return str(obj) return super().default(obj) def iterencode(self, obj): return super().iterencode(self._preprocess_date(obj))with open('output.json', 'w') as fo: json.dump(test, fo, cls=DateTimeEncoder)
This is obviously an expensive operation for larger dictionaries so some caution should be exercised. Also, would be nice for json.JSONEncoder
to be updated to use default on keys as well as values - issue created at : https://bugs.python.org/issue41569
you can try this :
class DatesToStrings(json.JSONEncoder): def _encode(self, obj): if isinstance(obj, dict): def transform_date(o): return self._encode(o.isoformat() if isinstance(o, datetime) else o) return {transform_date(k): transform_date(v) for k, v in obj.items()} else: return obj def encode(self, obj): return super(DatesToStrings, self).encode(self._encode(obj))
with open("output.json", "w") as fo: json.dump( json.dumps( test, cls=DatesToStrings), fo, cls=DatesToStrings)