Я хочу отправить объект datetime.datetime в сериализованной форме из Python с помощью JSON и десериализовать в JavaScript с помощью JSON. Как лучше всего это сделать?



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Если вы уверены, что JSON будет использовать только Javascript, я предпочитаю передавать объекты Javascript Date напрямую.
Метод ctime() для объектов datetime вернет строку, которую может понять объект Javascript Date.
import datetime
date = datetime.datetime.today()
json = '{"mydate":new Date("%s")}' % date.ctime()
Javascript с радостью будет использовать это как литерал объекта, и у вас есть встроенный объект Date.
Технически недействительный JSON, но это допустимый литерал объекта JavaScript. (Из принципа я бы установил Content-Type на text / javascript вместо application / json.) Если потребитель будет всегда, а навсегда будет Только реализацией JavaScript, то да, это довольно элегантно. Я бы использовал это.
.ctime() - ОЧЕНЬ плохой способ передачи информации о времени, .isoformat() намного лучше. .ctime() отбрасывает часовой пояс и переход на летнее время, как будто их не существует. Эта функция должна быть убита.
Годы спустя: пожалуйста, никогда не думайте об этом. Это будет работать только в том случае, если вы eval () ваш json в Javascript, что вам действительно не следует ...
Я обнаружил, что для кросс-языковых проектов лучше всего подходят строки, содержащие даты RFC 3339. Дата RfC 3339 выглядит так:
1985-04-12T23:20:50.52Z
Я думаю, что большая часть формата очевидна. Единственным несколько необычным может быть буква «Z» в конце. Это означает GMT / UTC. Вы также можете добавить смещение часового пояса, например +02: 00 для CEST (Германия летом). Лично я предпочитаю хранить все в формате UTC, пока оно не отобразится.
Для отображения, сравнения и хранения вы можете оставить его в строковом формате для всех языков. Если вам нужна дата для расчетов, легко преобразовать ее обратно в собственный объект даты на большинстве языков.
Итак, сгенерируйте JSON следующим образом:
json.dump(datetime.now().strftime('%Y-%m-%dT%H:%M:%SZ'))
К сожалению, конструктор Date в Javascript не принимает строки RfC 3339, но в Интернете доступно множество парсеры.
huTools.hujson пытается справиться с наиболее распространенными проблемами кодирования, с которыми вы можете столкнуться в коде Python, включая объекты даты / даты и времени, при правильной обработке часовых поясов.
Этот механизм форматирования даты изначально поддерживается как datetime: datetime.isoformat (), так и simplejson, который по умолчанию выгружает объекты datetime как строки isoformat. Нет необходимости в ручном взломе strftime.
@jrk - У меня не получается автоматическое преобразование объектов datetime в строку isoformat. Для меня simplejson.dumps(datetime.now()) дает TypeError: datetime.datetime(...) is not JSON serializable
json.dumps(datetime.datetime.now().isoformat()) - вот где происходит волшебство.
Прелесть simplejson в том, что если у меня сложная структура данных, он проанализирует ее и превратит в JSON. Если мне нужно выполнить json.dumps (datetime.datetime.now (). Isoformat ()) для каждого объекта datetime, я потеряю это. Есть ли способ исправить это?
superjoe30: как это сделать, см. stackoverflow.com/questions/455580/…
да, stackoverflow.com/questions/455580/… - лучшее решение
Самые последние конструкторы даты Javascript в настоящее время принимают строки RfC3339
Z для GMT / UTC - это код военной радиостанции, зулусское время: en.wikipedia.org/wiki/List_of_m military_time_zones.
Вы можете добавить параметр 'default' в json.dumps, чтобы справиться с этим:
date_handler = lambda obj: (
obj.isoformat()
if isinstance(obj, (datetime.datetime, datetime.date))
else None
)
json.dumps(datetime.datetime.now(), default=date_handler)
'"2010-04-20T20:08:21.634121"'
Это формат ISO 8601.
Более полная функция обработчика по умолчанию:
def handler(obj):
if hasattr(obj, 'isoformat'):
return obj.isoformat()
elif isinstance(obj, ...):
return ...
else:
raise TypeError, 'Object of type %s with value of %s is not JSON serializable' % (type(obj), repr(obj))
Обновление: добавлен вывод типа и значения. Обновление: также обрабатывать дату
это решение намного проще, чем у mdorseif. Отлично работает в моем приложении django.
Проблема в том, что если у вас есть другие объекты в list / dict, этот код преобразует их в None.
json.dumps тоже не знает, как их преобразовать, но исключение подавляется. К сожалению, однострочное исправление лямбда имеет свои недостатки. Если вы предпочитаете создавать исключение для неизвестных (что является хорошей идеей), используйте функцию, которую я добавил выше.
полный выходной формат также должен иметь часовой пояс ... а isoformat () не предоставляет эту функцию ... поэтому вы должны обязательно добавить эту информацию в строку перед возвратом
Это лучший способ. Почему это не было выбрано в качестве ответа?
Похоже, что в ответе есть ошибка - в последней строке, где возникает ошибка TypeError, часть подстановки строки (type (Obj), repr (Obj)) относится к чему-то с именем Obj, тогда как это должно быть obj (обратите внимание на заглавные буквы) .
отличный ответ, вы также можете получить метку времени, вы можете использовать что-то вроде этого: date2ts = lambda obj: time.mktime (obj.timetuple ()) if isinstance (obj, datetime.datetime) else None
Лучше поднять TypeError, когда тип не является datetime - иначе данные могут быть потеряны без вашего ведома!
Лямбда может быть адаптирована для вызова базовой реализации для типов, отличных от datetime, поэтому при необходимости может возникнуть ошибка TypeError: dthandler = lambda obj: obj.isoformat() if isinstance(obj, datetime) else json.JSONEncoder().default(obj)
Небольшое упрощение в однострочном лямбда-выражении: if isinstance(obj, (datetime.datetime, datetime.date)) устранит необходимость в or.
@NickFranceschina Я только что протестировал isoformat(), и он выводит смещение часового пояса. >>> datetime.datetime (2012, 10, 20, tzinfo = tzoffset (Нет, 3600 * 12)). isoformat () '2012-10-20T00: 00: 00 + 12: 00'
JSON from должен включать часовой пояс, и желательно UTC с буквой Z. Без этого время, подобное 2010-04-20T20: 08: 21.634121, будет считаться местным временем веб-браузера.
Используя json, вы можете создать подкласс JSONEncoder и переопределить метод default (), чтобы предоставить свои собственные сериализаторы:
import json
import datetime
class DateTimeJSONEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, datetime.datetime):
return obj.isoformat()
else:
return super(DateTimeJSONEncoder, self).default(obj)
Тогда вы можете назвать это так:
>>> DateTimeJSONEncoder().encode([datetime.datetime.now()])
'["2010-06-15T14:42:28"]'
Незначительное улучшение - использовать obj.isoformat(). Вы также можете использовать более распространенный вызов dumps(), который принимает другие полезные аргументы (например, indent): simplejson.dumps (myobj, cls = JSONEncoder, ...)
Потому что это вызовет родительский метод JSONEncoder, а не родительский метод DateTimeJSONEncoder. IE, вы поднялись бы на два уровня.
Вот довольно полное решение для рекурсивного кодирования и декодирования объектов datetime.datetime и datetime.date с использованием модуля стандартной библиотеки json. Для этого требуется Python> = 2.6, поскольку с тех пор код формата %f в строке формата datetime.datetime.strptime () поддерживается только в. Для поддержки Python 2.5 отбросьте %f и удалите микросекунды из строки даты ISO, прежде чем пытаться преобразовать ее, но, конечно, вы потеряете точность в микросекундах. Для взаимодействия со строками даты ISO из других источников, которые могут включать имя часового пояса или смещение UTC, вам также может потребоваться удалить некоторые части строки даты перед преобразованием. Полный анализатор строк даты ISO (и многих других форматов даты) см. В стороннем модуле dateutil.
Декодирование работает только тогда, когда строки даты ISO являются значениями в JavaScript. буквальное обозначение объекта или во вложенных структурах внутри объекта. Дата ISO строки, которые являются элементами массива верхнего уровня, будут декодированы нет.
Т.е. это работает:
date = datetime.datetime.now()
>>> json = dumps(dict(foo='bar', innerdict=dict(date=date)))
>>> json
'{"innerdict": {"date": "2010-07-15T13:16:38.365579"}, "foo": "bar"}'
>>> loads(json)
{u'innerdict': {u'date': datetime.datetime(2010, 7, 15, 13, 16, 38, 365579)},
u'foo': u'bar'}
И это тоже:
>>> json = dumps(['foo', 'bar', dict(date=date)])
>>> json
'["foo", "bar", {"date": "2010-07-15T13:16:38.365579"}]'
>>> loads(json)
[u'foo', u'bar', {u'date': datetime.datetime(2010, 7, 15, 13, 16, 38, 365579)}]
Но это не работает должным образом:
>>> json = dumps(['foo', 'bar', date])
>>> json
'["foo", "bar", "2010-07-15T13:16:38.365579"]'
>>> loads(json)
[u'foo', u'bar', u'2010-07-15T13:16:38.365579']
Вот код:
__all__ = ['dumps', 'loads']
import datetime
try:
import json
except ImportError:
import simplejson as json
class JSONDateTimeEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, (datetime.date, datetime.datetime)):
return obj.isoformat()
else:
return json.JSONEncoder.default(self, obj)
def datetime_decoder(d):
if isinstance(d, list):
pairs = enumerate(d)
elif isinstance(d, dict):
pairs = d.items()
result = []
for k,v in pairs:
if isinstance(v, basestring):
try:
# The %f format code is only supported in Python >= 2.6.
# For Python <= 2.5 strip off microseconds
# v = datetime.datetime.strptime(v.rsplit('.', 1)[0],
# '%Y-%m-%dT%H:%M:%S')
v = datetime.datetime.strptime(v, '%Y-%m-%dT%H:%M:%S.%f')
except ValueError:
try:
v = datetime.datetime.strptime(v, '%Y-%m-%d').date()
except ValueError:
pass
elif isinstance(v, (dict, list)):
v = datetime_decoder(v)
result.append((k, v))
if isinstance(d, list):
return [x[1] for x in result]
elif isinstance(d, dict):
return dict(result)
def dumps(obj):
return json.dumps(obj, cls=JSONDateTimeEncoder)
def loads(obj):
return json.loads(obj, object_hook=datetime_decoder)
if __name__ == '__main__':
mytimestamp = datetime.datetime.utcnow()
mydate = datetime.date.today()
data = dict(
foo = 42,
bar = [mytimestamp, mydate],
date = mydate,
timestamp = mytimestamp,
struct = dict(
date2 = mydate,
timestamp2 = mytimestamp
)
)
print repr(data)
jsonstring = dumps(data)
print jsonstring
print repr(loads(jsonstring))
Если вы напечатаете дату как datetime.datetime.utcnow().isoformat()[:-3]+"Z", она будет точно такой же, как то, что JSON.stringify () создает в javascript.
Я разобрался.
Допустим, у вас есть объект Python datetime, d, созданный с помощью datetime.now (). Его значение:
datetime.datetime(2011, 5, 25, 13, 34, 5, 787000)
Вы можете сериализовать его в JSON как строку даты и времени ISO 8601:
import json
json.dumps(d.isoformat())
Пример объекта datetime будет сериализован как:
'"2011-05-25T13:34:05.787000"'
Это значение, однажды полученное на уровне Javascript, может создать объект Date:
var d = new Date("2011-05-25T13:34:05.787000");
Начиная с Javascript 1.8.5, объекты Date имеют метод toJSON, который возвращает строку в стандартном формате. Следовательно, чтобы сериализовать указанный выше объект Javascript обратно в JSON, команда будет выглядеть так:
d.toJSON()
Что даст вам:
'2011-05-25T20:34:05.787Z'
Эта строка, однажды полученная в Python, может быть десериализована обратно в объект datetime:
datetime.strptime('2011-05-25T20:34:05.787Z', '%Y-%m-%dT%H:%M:%S.%fZ')
Это приводит к следующему объекту datetime, который является тем же самым, с которого вы начали, и, следовательно, правильным:
datetime.datetime(2011, 5, 25, 20, 34, 5, 787000)
На стороне питона:
import time, json
from datetime import datetime as dt
your_date = dt.now()
data = json.dumps(time.mktime(your_date.timetuple())*1000)
return data # data send to javascript
На стороне javascript:
var your_date = new Date(data)
где данные являются результатом Python
Мой совет - использовать библиотеку. Их несколько доступно на pypi.org.
Пользуюсь вот такой, работает хорошо: https://pypi.python.org/pypi/asjson
Хотя эта ссылка может дать ответ на вопрос, лучше включить сюда основные части ответа и предоставить ссылку для справки. Ответы, содержащие только ссылки, могут стать недействительными, если ссылка на страницу изменится. - Из обзора
Поздно в игре ... :)
Очень простое решение - исправить модуль json по умолчанию. Например:
import json
import datetime
json.JSONEncoder.default = lambda self,obj: (obj.isoformat() if isinstance(obj, datetime.datetime) else None)
Теперь вы можете использовать json.dumps (), как если бы он всегда поддерживал datetime ...
json.dumps({'created':datetime.datetime.now()})
Это имеет смысл, если вам требуется, чтобы это расширение модуля json всегда включалось, и вы не хотите изменять способ, которым вы или другие пользователи используют сериализацию json (в существующем коде или нет).
Обратите внимание, что некоторые могут посчитать такое исправление библиотек плохой практикой. Следует проявлять особую осторожность, если вы можете захотеть расширить свое приложение более чем одним способом - в таком случае я предлагаю использовать решение от ramen или JT и выбирать правильное расширение json в каждом случае.
Это незаметно поедает несериализуемые объекты и превращает их в None. Вместо этого вы можете создать исключение.
Не так много, чтобы добавить к ответу сообщества вики, кроме отметка времени!
Javascript использует следующий формат:
new Date().toJSON() // "2016-01-08T19:00:00.123Z"
Сторона Python (для обработчика json.dumps см. Другие ответы):
>>> from datetime import datetime
>>> d = datetime.strptime('2016-01-08T19:00:00.123Z', '%Y-%m-%dT%H:%M:%S.%fZ')
>>> d
datetime.datetime(2016, 1, 8, 19, 0, 0, 123000)
>>> d.isoformat() + 'Z'
'2016-01-08T19:00:00.123000Z'
Если вы не укажете Z, интерфейсные фреймворки, такие как angular, не смогут отображать дату в локальном часовом поясе браузера:
> $filter('date')('2016-01-08T19:00:00.123000Z', 'yyyy-MM-dd HH:mm:ss')
"2016-01-08 20:00:00"
> $filter('date')('2016-01-08T19:00:00.123000', 'yyyy-MM-dd HH:mm:ss')
"2016-01-08 19:00:00"
Очевидно, «Правильный» формат даты JSON (ну и JavaScript) - это 2012-04-23T18: 25: 43.511Z - UTC и "Z". Без этого JavaScript будет использовать местный часовой пояс веб-браузера при создании объекта Date () из строки.
Для «наивного» времени (то, что Python называет временем без часового пояса, и предполагается, что это локальный), ниже будет установить местный часовой пояс, чтобы затем его можно было правильно преобразовать в UTC:
def default(obj):
if hasattr(obj, "json") and callable(getattr(obj, "json")):
return obj.json()
if hasattr(obj, "isoformat") and callable(getattr(obj, "isoformat")):
# date/time objects
if not obj.utcoffset():
# add local timezone to "naive" local time
# https://stackoverflow.com/questions/2720319/python-figure-out-local-timezone
tzinfo = datetime.now(timezone.utc).astimezone().tzinfo
obj = obj.replace(tzinfo=tzinfo)
# convert to UTC
obj = obj.astimezone(timezone.utc)
# strip the UTC offset
obj = obj.replace(tzinfo=None)
return obj.isoformat() + "Z"
elif hasattr(obj, "__str__") and callable(getattr(obj, "__str__")):
return str(obj)
else:
print("obj:", obj)
raise TypeError(obj)
def dump(j, io):
json.dump(j, io, indent=2, default=default)
Почему это так сложно.
Для преобразования даты Python в JavaScript объект даты должен иметь определенный формат ISO, то есть формат ISO или номер UNIX. Если в формате ISO отсутствует какая-либо информация, вы можете сначала преобразовать в номер Unix с помощью Date.parse. Более того, Date.parse также работает с React, а новая дата может вызвать исключение.
Если у вас есть объект DateTime без миллисекунд, необходимо учитывать следующее. :
var unixDate = Date.parse('2016-01-08T19:00:00')
var desiredDate = new Date(unixDate).toLocaleDateString();
Дата в примере может также быть переменной в объекте result.data после вызова API.
Чтобы узнать о параметрах отображения даты в желаемом формате (например, для отображения длинных дней недели), ознакомьтесь с Документ MDN.
есть ли способ сделать это, например, python для массивов? У меня есть массив объектов datetime, и я хочу нарезать час
Вы предпочитаете использовать библиотеку или хотите сами написать код?