Is it possible to convert datetime into a readable JSON format (which could be used from javascript)? Currently jsonpickle provides only a binary encoded value for datetime.
After some trial and error I came up with the following solution:
class DatetimeHandler(jsonpickle.handlers.BaseHandler):
def flatten(self, obj, data):
return obj.strftime('%Y-%m-%d %H:%M:%S.%f')
jsonpickle.handlers.registry.register(datetime, DatetimeHandler)
encoded_datetime = jsonpickle.encode(datetime.now())
print(encoded_datetime)
decode_datetime = jsonpickle.decode(encoded_datetime)
print(decode_datetime)
There are several gotchas here:
First, please don't traffic in timezone unaware datetime objects. You will feel pain, not today, maybe not tomorrow, but someday. You can learn from others' mistakes (mine), or you can learn the hard way. As far as I'm concerned the fact that Python allows you to make datetime objects without a timezone at all is a bug.
Second, you can't reliably roundtrip strftime()
and strptime()
for timezone-aware datetime objects. This was fixed for UTC in Python 3.6, but still fails for other timezones.
Instead use datetime.isoformat()
and datetime.fromisoformat()
. These were added to the datetime
class in Python 3.7 (and backported to earlier versions).
Third, jsonpickle
does not clearly document how to roll your own DatetimeHandler
. So yeah, you just want something legible that you send to Javascript or whatever? The solutions above will be fine. You want something that's legible but you also want to pull it back into Python at some point? Um, trickier.
Here's a hint: when you are subclassing a library to extend its capability, look carefully at the superclass you are extending.
I would have written DatetimeHandler
somewhat differently. But the following works, and contains all my hard won wisdom on the subject. Ouch.
import pytz
import jsonpickle
from datetime import datetime
class Blah(object):
def __init__(self, blah):
self.datetime = datetime.now(pytz.utc)
self.blah = blah
def to_json(self):
return jsonpickle.encode(self)
@classmethod
def from_json(cls, json_str):
return jsonpickle.decode(json_str)
class DatePickleISO8601(jsonpickle.handlers.DatetimeHandler):
def flatten(self, obj, data):
pickler = self.context
if not pickler.unpicklable:
return str(obj)
cls, args = obj.__reduce__()
flatten = pickler.flatten
payload = obj.isoformat()
args = [payload] + [flatten(i, reset=False) for i in args[1:]]
data['__reduce__'] = (flatten(cls, reset=False), args)
return data
def restore(self, data):
cls, args = data['__reduce__']
unpickler = self.context
restore = unpickler.restore
cls = restore(cls, reset=False)
value = datetime.fromisoformat(args[0])
return value
jsonpickle.handlers.registry.register(datetime, DatePickleISO8601)
jsonpickle
is that, as its name suggests, it is an analogue of pickle
, so reversibility is implied and important. –
Prelate jsonpickle
renders as "date": {"py/object": "datetime.date", "__reduce__": [{"py/type": "datetime.date"}, ["B+ULEA=="]]}
. The only suggestion on this page that works for me is unpicklable=False
, but it causes the data returned to be massive — 100Mb or more. None of the others have any effect. Any suggestions? –
Frenchman With current version of jsonpickle (obtained from pip as of today), it seems plain use of encode just works, if setting unplickable to false:
>>> import jsonpickle
>>> from datetime import datetime
>>> jsonpickle.encode(datetime.now(), unpicklable=False)
'"2014-05-25 20:24:30.357299"'
However, your trick can be used to produce ISO 8601 format, which could be more adequate, accoding to ECMAScript v5 specs:
>>> class DatetimeHandler(jsonpickle.handlers.BaseHandler):
... def flatten(self, obj, data):
... return obj.isoformat()
...
>>> jsonpickle.handlers.registry.register(datetime, DatetimeHandler)
>>> jsonpickle.encode(datetime.now(), unpicklable=False)
'"2014-05-25T20:31:30.422826"'
After some trial and error I came up with the following solution:
class DatetimeHandler(jsonpickle.handlers.BaseHandler):
def flatten(self, obj, data):
return obj.strftime('%Y-%m-%d %H:%M:%S.%f')
jsonpickle.handlers.registry.register(datetime, DatetimeHandler)
encoded_datetime = jsonpickle.encode(datetime.now())
print(encoded_datetime)
decode_datetime = jsonpickle.decode(encoded_datetime)
print(decode_datetime)
© 2022 - 2024 — McMap. All rights reserved.