Я хочу отправить объект datetime.datetime в сериализованной форме из Python, используя JSON, и десериализовать в JavaScript, используя JSON. Каков наилучший способ сделать это?
Я хочу отправить объект datetime.datetime в сериализованной форме из Python, используя JSON, и десериализовать в JavaScript, используя JSON. Каков наилучший способ сделать это?
Ответы:
Вы можете добавить параметр '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))
Обновление: добавлен вывод типа и значения.
Обновление: также обрабатывать дату
dthandler = lambda obj: obj.isoformat() if isinstance(obj, datetime) else json.JSONEncoder().default(obj)
Для кросс-языковых проектов я обнаружил, что строки с датами 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 (), так и by simplejson
, который по умолчанию создает дамп datetime
объектов в виде isoformat
строк. Нет необходимости в ручном strftime
взломе.
datetime
объектов в isoformat
строку. Для меня simplejson.dumps(datetime.now())
урожайностьTypeError: datetime.datetime(...) is not JSON serializable
json.dumps(datetime.datetime.now().isoformat())
Здесь происходит волшебство.
Я решил это.
Допустим, у вас есть объект datetime Python, 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)
Используя 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, ...)
Вот довольно полное решение для рекурсивного кодирования и декодирования объектов 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
Если вы уверены, что только Javascript будет использовать JSON, я предпочитаю передавать Date
объекты Javascript напрямую.
ctime()
Метод на datetime
объектах возвращает строку , что дата объект Javascript может понять.
import datetime
date = datetime.datetime.today()
json = '{"mydate":new Date("%s")}' % date.ctime()
Javascript с радостью будет использовать его в качестве литерала объекта, и у вас уже есть встроенный объект Date.
.ctime()
это ОЧЕНЬ плохой способ передачи информации о времени, .isoformat()
гораздо лучше. Что .ctime()
отбрасывает часовой пояс и летнее время, как будто их не существует. Эта функция должна быть убита.
Поздно в игре ... :)
Очень простое решение - установить исправление по умолчанию для модуля 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"
Мой совет - использовать библиотеку. Есть несколько доступных на pypi.org.
Я использую этот, он работает хорошо: https://pypi.python.org/pypi/asjson
Видимо, «правильным» форматом даты 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
# /programming/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, в то время как новый Date может вызвать исключение.
Если у вас есть объект DateTime без миллисекунд, необходимо учитывать следующее. :
var unixDate = Date.parse('2016-01-08T19:00:00')
var desiredDate = new Date(unixDate).toLocaleDateString();
Примерная дата может также быть переменной в объекте result.data после вызова API.
Для вариантов отображения даты в желаемом формате (например, для отображения длинных дней недели) ознакомьтесь с документом MDN .