jsonpickle datetime в читаемый формат json

Можно ли конвертировать datetime в читаемый формат JSON (который можно использовать из javascript)? В настоящее время jsonpickle предоставляет только двоичное значение для datetime.

3 ответа

Решение

После некоторых проб и ошибок я пришел к следующему решению:

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)

Здесь есть несколько ошибок:

Во-первых, пожалуйста, не торгуйте в часовых поясах, не зная объекты даты и времени. Вы будете чувствовать боль не сегодня, может быть, не завтра, а когда-нибудь. Вы можете учиться на чужих ошибках (моих), или вы можете учиться трудным путем. Что касается меня, то, что Python позволяет вам создавать объекты даты и времени без часового пояса, является ошибкой.

Во-вторых, strptime не обрабатывает часовые пояса! Таким образом, вы поступаете правильно, а затем записываете свою дату в каком-то формате и хотите прочитать ее обратно, и kablooey, вы получаете "%z bad formatting string". Grr.

Используйте библиотеку iso8601 для разбора ваших строк даты и времени ISO8601. Часовые пояса обрабатываются.

В-третьих, jsonpickle не содержит четких документов о том, как накатывать свой собственный DatetimeHandler. Так что да, вы просто хотите что-то разборчивое, что вы отправляете в Javascript или что-то еще? Решения выше будет хорошо. Вы хотите что-то разборчивое, но в какой-то момент вы хотите вернуть его обратно в Python? Хм, сложнее.

Вот подсказка: когда вы наследуете библиотеку для расширения ее возможностей, внимательно посмотрите на суперкласс, который вы расширяете.

Я бы написал DatetimeHandler несколько иначе. Но следующие работы, и содержит всю мою трудно завоеванную мудрость по этому вопросу. Уч.

import pytz
import jsonpickle
import iso8601
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 unicode(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 = iso8601.parse_date(args[0])
        return value

jsonpickle.handlers.registry.register(datetime, DatePickleISO8601)

С текущей версией jsonpickle (полученной из pip на сегодняшний день), кажется, простое использование кодирования просто работает, если установить unplickable в false:

>>> import jsonpickle
>>> from datetime import datetime
>>> jsonpickle.encode(datetime.now(), unpicklable=False)
'"2014-05-25 20:24:30.357299"'

Однако ваш трюк можно использовать для создания формата ISO 8601, который может быть более адекватным, в соответствии со спецификациями ECMAScript v5:

>>> 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"'
Другие вопросы по тегам