Desejo enviar um objeto datetime.datetime no formato serializado do Python usando JSON e desserializar no JavaScript usando JSON. Qual é a melhor maneira de fazer isso?
Desejo enviar um objeto datetime.datetime no formato serializado do Python usando JSON e desserializar no JavaScript usando JSON. Qual é a melhor maneira de fazer isso?
Respostas:
Você pode adicionar o parâmetro 'default' ao json.dumps para lidar com isso:
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"'
Uma função de manipulador padrão mais abrangente:
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))
Atualização: saída adicionada do tipo e do valor.
Atualização: também lida com a data
dthandler = lambda obj: obj.isoformat() if isinstance(obj, datetime) else json.JSONEncoder().default(obj)
Para projetos em vários idiomas, descobri que as seqüências contendo datas RfC 3339 são o melhor caminho a percorrer. Uma data da RfC 3339 é assim:
1985-04-12T23:20:50.52Z
Eu acho que a maior parte do formato é óbvia. A única coisa um tanto incomum pode ser o "Z" no final. Significa GMT / UTC. Você também pode adicionar um deslocamento de fuso horário como +02: 00 para CEST (Alemanha no verão). Pessoalmente, prefiro manter tudo no UTC até que seja exibido.
Para exibição, comparações e armazenamento, você pode deixá-lo no formato de sequência em todos os idiomas. Se você precisar da data para cálculos, é fácil convertê-la novamente em um objeto de data nativo na maioria dos idiomas.
Portanto, gere o JSON assim:
json.dump(datetime.now().strftime('%Y-%m-%dT%H:%M:%SZ'))
Infelizmente, o construtor Date do Javascript não aceita seqüências de caracteres RfC 3339, mas existem muitos analisadores disponíveis na Internet.
O huTools.hujson tenta lidar com os problemas de codificação mais comuns que você pode encontrar no código Python, incluindo objetos de data / data e hora enquanto manipula fusos horários corretamente.
datetime
: datetime.isoformat () quanto por simplejson
, que irá despejar datetime
objetos como isoformat
seqüências de caracteres por padrão. Não há necessidade de strftime
hackers manuais .
datetime
objetos para a isoformat
seqüência de caracteres. Para mim, simplejson.dumps(datetime.now())
os rendimentosTypeError: datetime.datetime(...) is not JSON serializable
json.dumps(datetime.datetime.now().isoformat())
é onde a mágica acontece.
Eu resolvi isso.
Digamos que você tenha um objeto datetime do Python, d , criado com datetime.now (). Seu valor é:
datetime.datetime(2011, 5, 25, 13, 34, 5, 787000)
Você pode serializá-lo para JSON como uma sequência de data e hora ISO 8601:
import json
json.dumps(d.isoformat())
O exemplo de objeto datetime seria serializado como:
'"2011-05-25T13:34:05.787000"'
Esse valor, uma vez recebido na camada Javascript, pode construir um objeto Date:
var d = new Date("2011-05-25T13:34:05.787000");
A partir do Javascript 1.8.5, os objetos Date têm um método toJSON, que retorna uma string em um formato padrão. Para serializar o objeto Javascript acima de volta para JSON, portanto, o comando seria:
d.toJSON()
O que lhe daria:
'2011-05-25T20:34:05.787Z'
Essa sequência, uma vez recebida em Python, pode ser desserializada de volta para um objeto datetime:
datetime.strptime('2011-05-25T20:34:05.787Z', '%Y-%m-%dT%H:%M:%S.%fZ')
Isso resulta no seguinte objeto datetime, que é o mesmo com o qual você iniciou e, portanto, correto:
datetime.datetime(2011, 5, 25, 20, 34, 5, 787000)
Usando json
, você pode subclassificar JSONEncoder e substituir o método default () para fornecer seus próprios serializadores personalizados:
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)
Então, você pode chamar assim:
>>> DateTimeJSONEncoder().encode([datetime.datetime.now()])
'["2010-06-15T14:42:28"]'
obj.isoformat()
. Você também pode usar o mais comum dumps()
chamada, que tem outros argumentos úteis (como indent
): simplejson.dumps (myObj, cls = JSONEncoder, ...)
Aqui está uma solução bastante completa para codificar e decodificar recursivamente os objetos datetime.datetime e datetime.date usando o json
módulo de biblioteca padrão . Isso precisa do Python> = 2.6, pois o %f
código do formato datetime.datetime.strptime () é suportado somente desde então. Para suporte ao Python 2.5, solte %f
e retire os microssegundos da sequência de datas ISO antes de tentar convertê-lo, mas você perderá a precisão dos microssegundos, é claro. Para interoperabilidade com seqüências de datas ISO de outras fontes, que podem incluir um nome de fuso horário ou deslocamento UTC, também é necessário remover algumas partes da sequência de datas antes da conversão. Para um analisador completo de seqüências de datas ISO (e muitos outros formatos de data), consulte o módulo dateutil de terceiros .
A decodificação funciona apenas quando as cadeias de datas ISO são valores em uma notação literal de objeto JavaScript ou em estruturas aninhadas em um objeto. As sequências de datas ISO, que são itens de uma matriz de nível superior, não serão decodificadas.
Ou seja, isso funciona:
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'}
E isso também:
>>> 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)}]
Mas isso não funciona conforme o esperado:
>>> 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']
Aqui está o código:
__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"
se fosse exatamente o que JSON.stringify () produz em javascript
Se você tiver certeza de que apenas o Javascript consumirá o JSON, prefiro transmitir Date
objetos Javascript diretamente.
O ctime()
método nos datetime
objetos retornará uma sequência que o objeto Data Javascript possa entender.
import datetime
date = datetime.datetime.today()
json = '{"mydate":new Date("%s")}' % date.ctime()
O Javascript o utilizará como um literal de objeto, e você terá seu objeto Date incorporado.
.ctime()
é uma maneira MUITO ruim de passar informações de tempo, .isoformat()
é muito melhor. O que .ctime()
faz é jogar fora o fuso horário e o horário de verão como se eles não existissem. Essa função deve ser eliminada.
Tarde no jogo ... :)
Uma solução muito simples é corrigir o padrão do módulo json. Por exemplo:
import json
import datetime
json.JSONEncoder.default = lambda self,obj: (obj.isoformat() if isinstance(obj, datetime.datetime) else None)
Agora, você pode usar json.dumps () como se ele sempre tivesse suporte em data e hora ...
json.dumps({'created':datetime.datetime.now()})
Isso faz sentido se você precisar que essa extensão do módulo json sempre entre em ação e deseje não alterar a maneira como você ou outras pessoas usam a serialização json (no código existente ou não).
Observe que alguns podem considerar o patch de bibliotecas dessa maneira como uma má prática. Cuidados especiais devem ser tomados caso você queira estender seu aplicativo de mais de uma maneira - nesse caso, sugiro usar a solução por ramen ou JT e escolher a extensão json apropriada em cada caso.
None
. Você pode lançar uma exceção.
Não há muito a acrescentar à resposta do wiki da comunidade, exceto o carimbo de data e hora !
Javascript usa o seguinte formato:
new Date().toJSON() // "2016-01-08T19:00:00.123Z"
Lado do Python (para o json.dumps
manipulador, veja as outras respostas):
>>> 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'
Se você deixar esse Z de fora, estruturas de front-end como angular não poderão exibir a data no fuso horário local do navegador:
> $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"
Meu conselho é usar uma biblioteca. Existem vários disponíveis em pypi.org.
Eu uso este aqui, ele funciona bem: https://pypi.python.org/pypi/asjson
Aparentemente, o formato de data JSON "correto" (bem JavaScript) é 2012-04-23T18: 25: 43.511Z - UTC e "Z". Sem esse JavaScript, o fuso horário do navegador da Web será usado ao criar um objeto Date () a partir da string.
Por um horário "ingênuo" (o que o Python chama de horário sem fuso horário e isso pressupõe que é local), o abaixo forçará o fuso horário local para que possa ser convertido corretamente no 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)
Por que isso é tão difícil.
Para a conversão de data do Python para JavaScript, o objeto de data precisa estar no formato ISO específico, ou seja, no formato ISO ou no número UNIX. Se o formato ISO não tiver algumas informações, você poderá converter para o número Unix com Date.parse primeiro. Além disso, o Date.parse também funciona com o React, enquanto o novo Date pode acionar uma exceção.
Caso você tenha um objeto DateTime sem milissegundos, é necessário considerar o seguinte. :
var unixDate = Date.parse('2016-01-08T19:00:00')
var desiredDate = new Date(unixDate).toLocaleDateString();
A data do exemplo pode ser igualmente uma variável no objeto result.data após uma chamada de API.
Para opções para exibir a data no formato desejado (por exemplo, para exibir dias úteis prolongados), consulte o documento MDN .