Como superar o "datetime.datetime não JSON serializable"?


742

Eu tenho um ditado básico da seguinte maneira:

sample = {}
sample['title'] = "String"
sample['somedate'] = somedatetimehere

Quando tento fazer jsonify(sample), recebo:

TypeError: datetime.datetime(2012, 8, 8, 21, 46, 24, 862000) is not JSON serializable

O que posso fazer para que meu exemplo de dicionário possa superar o erro acima?

Nota: Embora possa não ser relevante, os dicionários são gerados a partir da recuperação de registros fora de mongodbonde quando eu imprimo str(sample['somedate']), a saída é 2012-08-08 21:46:24.862000.


1
Isso é especificamente python em geral, ou possivelmente django?
JDI

1
Tecnicamente, é especificamente python, não estou usando django, mas recuperando registros do mongodb.
Rolando


Estou usando o mongoengine, mas se o pymongo tiver maneiras melhores de contornar ou superar isso, informe.
Rolando

3
A questão vinculada está essencialmente dizendo para você não tentar serializar o objeto datetime, mas convertê-lo em uma string no formato ISO comum antes de serializar.
9788 Thomas Kelley às

Respostas:


377

Atualizado para 2018

A resposta original acomodava a maneira como os campos de "data" do MongoDB eram representados como:

{"$date": 1506816000000}

Se você deseja uma solução Python genérica para serializar datetimepara json, consulte a resposta do @jjmontes para obter uma solução rápida que não requer dependências.


Como você está usando o mongoengine (por comentários) e o pymongo é uma dependência, o pymongo possui utilitários internos para ajudar na serialização do json:
http://api.mongodb.org/python/1.10.1/api/bson/json_util.html

Exemplo de uso (serialização):

from bson import json_util
import json

json.dumps(anObject, default=json_util.default)

Exemplo de uso (desserialização):

json.loads(aJsonString, object_hook=json_util.object_hook)

Django

O Django fornece um DjangoJSONEncoderserializador nativo que lida com esse tipo de maneira adequada.

Consulte https://docs.djangoproject.com/en/dev/topics/serialization/#djangojsonencoder

from django.core.serializers.json import DjangoJSONEncoder

return json.dumps(
  item,
  sort_keys=True,
  indent=1,
  cls=DjangoJSONEncoder
)

Uma diferença que eu notei entre DjangoJSONEncodere usando um costume defaultcomo este:

import datetime
import json

def default(o):
    if isinstance(o, (datetime.date, datetime.datetime)):
        return o.isoformat()

return json.dumps(
  item,
  sort_keys=True,
  indent=1,
  default=default
)

O Django retira um pouco dos dados:

 "last_login": "2018-08-03T10:51:42.990", # DjangoJSONEncoder 
 "last_login": "2018-08-03T10:51:42.990239", # default

Portanto, talvez seja necessário ter cuidado com isso em alguns casos.


3
É uma boa / má prática misturar várias bibliotecas, ou seja, ter mongoengine para inserir documentos e pymongo para consulta / recuperação?
Rolando

Não é uma prática ruim, apenas implica alguma dependência das bibliotecas que sua biblioteca principal usa. Se você não conseguir o que precisa da mongoengina, desça para o pymongo. É o mesmo com Django MongoDB. Com o posterior, você tentaria permanecer no ORM do django para manter o estado agnóstico do back-end. Mas, às vezes, você não pode fazer o que precisa na abstração, então você desce uma camada. Nesse caso, isso não está relacionado ao seu problema, pois você está apenas usando métodos utilitários para acompanhar o formato JSON.
JDI

Estou tentando fazer isso com o Flask e parece que, usando o json.dump, não consigo colocar um wrapper jsonify () em torno dele, para que ele retorne no aplicativo / json. Tentativa de retornar o jsonify (json.dumps (amostra, padrão = json_util.default))
Rolando

2
@amit Não se trata tanto de memorizar a sintaxe, mas de melhorar a leitura da documentação e armazenar informações suficientes na minha cabeça para reconhecer onde e quando preciso recuperá-la novamente. Neste caso, pode-se dizer "Oh um objeto personalizado com json" e, em seguida, atualizar rapidamente sobre esse uso
JDI

2
@guyskk Não acompanhei mudanças no bjson ou no mongo desde que escrevi isso há 5 anos. Mas se você quer controle sobre a serialização da data e hora, então você precisa escrever sua própria função manipulador padrão, como ilustrado na resposta dada pelo jgbarah
JDI

619

Meu dump JSON rápido e sujo que come datas e tudo:

json.dumps(my_dictionary, indent=4, sort_keys=True, default=str)

14
Isso é incrível, mas infelizmente eu não entendi o que aconteceu? Alguém pode explicar esta resposta?
Kishor Pawar #

63
@KishorPawar: defaulté uma função aplicada a objetos que não são serializáveis. Nesse caso, é str, portanto, apenas converte tudo o que não sabe em seqüências de caracteres. O que é ótimo para serialização, mas não é tão bom ao desserializar (daí o "rápido e sujo") quanto qualquer coisa pode ter sido digitada sem aviso, por exemplo, uma função ou matriz numpy.
Mark

1
@Mark awesome. Obrigado. Útil quando você conhece o tipo desses valores não serializáveis, como datas.
Kishor Pawar

2
Por que passei a vida toda sem saber disso. :)
Arel

1
@jjmontes, não funciona para tudo, por exemplo , json.dumps({():1,type(None):2},default=str)aumentos TypeError, não pode ter tipo ou tupla.
Alancalvitti

443

Com base em outras respostas, uma solução simples baseada em um serializador específico que apenas converte datetime.datetimee datetime.dateobjetos em seqüências de caracteres.

from datetime import date, datetime

def json_serial(obj):
    """JSON serializer for objects not serializable by default json code"""

    if isinstance(obj, (datetime, date)):
        return obj.isoformat()
    raise TypeError ("Type %s not serializable" % type(obj))

Como visto, o código apenas verifica se o objeto é da classe datetime.datetimeou datetime.date, e depois usa .isoformat()para produzir uma versão serializada, de acordo com o formato ISO 8601, AAAA-MM-DDTHH: MM: SS (que é facilmente decodificado por JavaScript ) Se representações serializadas mais complexas forem buscadas, outro código poderá ser usado em vez de str () (consulte outras respostas a esta pergunta para exemplos). O código termina levantando uma exceção, para lidar com o caso em que é chamado com um tipo não serializável.

Essa função json_serial pode ser usada da seguinte maneira:

from datetime import datetime
from json import dumps

print dumps(datetime.now(), default=json_serial)

Os detalhes sobre como o parâmetro padrão do json.dumps funciona podem ser encontrados na Seção Uso Básico da documentação do módulo json .


5
sim a resposta correta, data e hora mais bonita de importação e se isinstance (obj, datetime.datetime), perdi muitos tempo, porque não utilizados a partir de data e hora de data e hora de importação, de qualquer forma obrigado
Sérgio

12
mas isso não explica como desserializar com o tipo correto, não é?
BlueTrin

2
Não, @BlueTrin, nada disse sobre isso. No meu caso, estou desserializando em JavaScript, que funciona imediatamente.
jgbarah

1
Isso causará um comportamento inesperado se o módulo json for atualizado para incluir a serialização de objetos de data e hora.
23716 Justin justin

1
@serg Mas a conversão dos tempos para o UTC seria unificada 01:00:00+01:00e 02:00:00+00:00que não deve ser a mesma, dependendo do contexto. Eles se referem ao mesmo ponto no tempo, é claro, mas o deslocamento pode ser um aspecto relevante do valor.
Alfe

211

Acabei de encontrar esse problema e minha solução é subclassificar json.JSONEncoder:

from datetime import datetime
import json

class DateTimeEncoder(json.JSONEncoder):
    def default(self, o):
        if isinstance(o, datetime):
            return o.isoformat()

        return json.JSONEncoder.default(self, o)

Na sua ligação, faça algo como: json.dumps(yourobj, cls=DateTimeEncoder)O .isoformat()que recebi de uma das respostas acima.


22
aumentou porque a implementação de um JSONEncoder personalizado deve ser a maneira correta de ir
3k-

25
Essa não é apenas a resposta principal, mas também parte do codificador json comum. Se ao menos a decodificação fosse menos ambígua ..
Joost

4
Para quem usa o Django, veja DjangoJSONEncoder. docs.djangoproject.com/en/dev/topics/serialization/…
S. Kirby

4
Super útil. Última linha poderia serreturn super(DateTimeEncoder, self).default(o)
Bob Stein

16
Com o Python 3, a última linha é ainda mais simples:return super().default(o)
ariddell 15/01

124

Converter a data em uma sequência

sample['somedate'] = str( datetime.utcnow() )

10
E como eu poderia desserializá-lo em Python?
Wobmene

62
O problema é se você possui muitos objetos datetime incorporados profundamente em uma estrutura de dados ou eles são aleatórios. Este não é um método confiável.
Rebs

3
desserializar: oDate = datetime.datetime.strptime(sDate, '%Y-%m-%d %H:%M:%S.%f'). Formatos obtidos em: docs.python.org/2/library/datetime.html
Roman

13
Voto negativo, pois ignora as informações do fuso horário. Lembre-se de que .now()usa a hora local, sem indicar isso. Pelo menos .utcnow()deveria ser utilizada (e, em seguida, um ou Z 0000 anexa)
Daniel F

1
@DanielF At least .utcnow() should be usedNão exatamente, datetime.now(timezone.utc)é recomendado, consulte o aviso em: docs.python.org/3.8/library/… .
Toreno96

79

Para outras pessoas que não precisam ou desejam usar a biblioteca pymongo para isso, é possível obter facilmente a conversão JSON de data e hora com este pequeno trecho:

def default(obj):
    """Default JSON serializer."""
    import calendar, datetime

    if isinstance(obj, datetime.datetime):
        if obj.utcoffset() is not None:
            obj = obj - obj.utcoffset()
        millis = int(
            calendar.timegm(obj.timetuple()) * 1000 +
            obj.microsecond / 1000
        )
        return millis
    raise TypeError('Not sure how to serialize %s' % (obj,))

Em seguida, use-o assim:

import datetime, json
print json.dumps(datetime.datetime.now(), default=default)

resultado: 

'1365091796124'

1
Não deve millis=ser recuado dentro da instrução if? Provavelmente também é melhor usar str (obj) para obter o formato ISO que eu acho mais comum.
Rebs

Por que você gostaria que fosse recuado? Esse snippet funciona e a saída resultante pode ser facilmente desserializada / analisada a partir de javascript.
Jay Taylor

5
Porque obj pode não ser um objeto [hora, data e data e hora]
Rebs

2
seu exemplo está incorreto se o fuso horário local tiver deslocamento UTC diferente de zero (a maioria deles). datetime.now()retorna a hora local (como um objeto de data e hora ingênuo), mas seu código assume que objestá no UTC se não estiver ciente do fuso horário. Use em datetime.utcnow()vez disso.
JFS

1
Ajuste-o para gerar um erro de tipo se obj não for reconhecido de acordo com a recomendação de documentação do Python em docs.python.org/2/library/json.html#basic-usage .
Jay Taylor

40

Aqui está a minha solução:

# -*- coding: utf-8 -*-
import json


class DatetimeEncoder(json.JSONEncoder):
    def default(self, obj):
        try:
            return super(DatetimeEncoder, obj).default(obj)
        except TypeError:
            return str(obj)

Então você pode usá-lo assim:

json.dumps(dictionnary, cls=DatetimeEncoder)

aceita. Muito melhor, pelo menos fora do contexto do mongodb. Você pode fazer isinstance(obj, datetime.datetime)dentro do TypeError, adicionar mais tipos para manipular e finalizar com o str(obj)ou repr(obj). E todos os seus despejos podem apenas apontar para essa classe especializada.
JL Peyret

@ Natim, esta solução é a melhor. 1
Souvik Ray 22/11/19

20

Eu tenho um aplicativo com um problema semelhante; minha abordagem foi JSONize o valor datetime como uma lista de 6 itens (ano, mês, dia, hora, minutos, segundos); você poderia ir para microssegundos como uma lista de 7 itens, mas eu não precisava:

class DateTimeEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, datetime.datetime):
            encoded_object = list(obj.timetuple())[0:6]
        else:
            encoded_object =json.JSONEncoder.default(self, obj)
        return encoded_object

sample = {}
sample['title'] = "String"
sample['somedate'] = datetime.datetime.now()

print sample
print json.dumps(sample, cls=DateTimeEncoder)

produz:

{'somedate': datetime.datetime(2013, 8, 1, 16, 22, 45, 890000), 'title': 'String'}
{"somedate": [2013, 8, 1, 16, 22, 45], "title": "String"}

Falha ao trabalho se o tempo economizado é salvo por fazer datetime.utcnow ()
saurshaz

1
Que erro você está vendo com o datetime.utcnow ()? Funciona bem para mim.
Codigo

17

Minha solução (com menos verbosidade, eu acho):

def default(o):
    if type(o) is datetime.date or type(o) is datetime.datetime:
        return o.isoformat()

def jsondumps(o):
    return json.dumps(o, default=default)

Em seguida, use em jsondumpsvez de json.dumps. Irá imprimir:

>>> jsondumps({'today': datetime.date.today()})
'{"today": "2013-07-30"}'

Eu quero, mais tarde você pode adicionar outros casos especiais a isso com um simples toque no defaultmétodo. Exemplo:

def default(o):
    if type(o) is datetime.date or type(o) is datetime.datetime:
        return o.isoformat()
    if type(o) is decimal.Decimal:
        return float(o)

1
Você deve usar isinstance (o, (datetime.date, datetime.datetime,)). Provavelmente não faria mal incluir também datetime.time.
Rebs

Acho que essa não é mais uma boa solução. Provavelmente, as conversões devem ter um lugar mais privilegiado - e também mais compreensível - em seu código, para que você saiba no que está convertendo quando coloca coisas em um banco de dados ou o que quer que seja, em vez de fazer tudo por um função transparente. Mas eu não sei.
Fiatjaf

1
JSON é bom para serializar dados para processamento posterior. Você pode não saber exatamente quais são esses dados. E você não deveria precisar. Serializar JSON deve funcionar. Assim como a conversão de unicode para ascii deveria. A incapacidade do Python de fazer isso sem funções obscuras torna irritante o uso. A validação de banco de dados é uma questão separada da IMO.
Rebs

Não, não deveria "apenas funcionar". Se você não sabe como ocorreu a serialização e precisa acessar os dados posteriormente de outro programa / idioma, está perdido.
Fiatjaf 12/05

2
JSON é comumente usado para strings, ints, floats, datas (tenho certeza que outros usam moeda, temperaturas, geralmente também). Mas datetime faz parte da biblioteca padrão e deve oferecer suporte à des / serialização. Se não fosse por essa questão, eu ainda estaria procurando manualmente minhas bolhas JSON incrivelmente complexos (que eu nem sempre criar a estrutura) para datas e serializadas-los 1 por 1.
Rebs

16

Este Q repete uma e outra vez - uma maneira simples de corrigir o módulo json de forma que a serialização suporte a data e hora.

import json
import datetime

json.JSONEncoder.default = lambda self,obj: (obj.isoformat() if isinstance(obj, datetime.datetime) else None)

Em seguida, use a serialização json como sempre - desta vez com o datetime sendo serializado como isoformato.

json.dumps({'created':datetime.datetime.now()})

Resultando em: '{"created": "26-08-2015T14: 21: 31.853855"}'

Veja mais detalhes e algumas palavras de cautela em: StackOverflow: JSON datetime entre Python e JavaScript


Patch de macaco FTW. A coisa mais desagradável é que isso modifica o comportamento do módulo json em todo o aplicativo, o que pode surpreender os outros em um aplicativo grande; portanto, geralmente deve ser usado com cuidado.
Jaap Versteegh

15

O método json.dumps pode aceitar um parâmetro opcional chamado default, que se espera seja uma função. Toda vez que o JSON tenta converter um valor, ele não sabe como convertê-lo, chamará a função que passamos para ele. A função receberá o objeto em questão e espera-se retornar a representação JSON do objeto.

def myconverter(o):
 if isinstance(o, datetime.datetime):
    return o.__str__()

print(json.dumps(d, default = myconverter)) 

14

se você estiver usando python3.7, a melhor solução será usar datetime.isoformat()and datetime.fromisoformat(); eles trabalham com datetimeobjetos ingênuos e conscientes :

#!/usr/bin/env python3.7

from datetime import datetime
from datetime import timezone
from datetime import timedelta
import json

def default(obj):
    if isinstance(obj, datetime):
        return { '_isoformat': obj.isoformat() }
    return super().default(obj)

def object_hook(obj):
    _isoformat = obj.get('_isoformat')
    if _isoformat is not None:
        return datetime.fromisoformat(_isoformat)
    return obj

if __name__ == '__main__':
    #d = { 'now': datetime(2000, 1, 1) }
    d = { 'now': datetime(2000, 1, 1, tzinfo=timezone(timedelta(hours=-8))) }
    s = json.dumps(d, default=default)
    print(s)
    print(d == json.loads(s, object_hook=object_hook))

resultado:

{"now": {"_isoformat": "2000-01-01T00:00:00-08:00"}}
True

se você estiver usando python3.6 ou abaixo, e você só se preocupam com o valor de tempo (não o fuso horário), então você pode usar datetime.timestamp()e datetime.fromtimestamp(), em vez;

se você estiver usando python3.6 ou abaixo, e se importa com o fuso horário, pode obtê-lo via datetime.tzinfo, mas é necessário serializar esse campo sozinho; a maneira mais fácil de fazer isso é adicionar outro campo _tzinfono objeto serializado;

finalmente, cuidado com as precisões em todos esses exemplos;


datetime.isoformat () também está presente no Python 2.7: docs.python.org/2/library/…
powlo

11

Você deve usar o .strftime()método no .datetime.now()método para torná-lo como um método serializável .

Aqui está um exemplo:

from datetime import datetime

time_dict = {'time': datetime.now().strftime('%Y-%m-%dT%H:%M:%S')}
sample_dict = {'a': 1, 'b': 2}
sample_dict.update(time_dict)
sample_dict

Resultado:

Out[0]: {'a': 1, 'b': 2, 'time': '2017-10-31T15:16:30'}

10

Aqui está uma solução simples para superar o problema "datetime não JSON serializable".

enco = lambda obj: (
    obj.isoformat()
    if isinstance(obj, datetime.datetime)
    or isinstance(obj, datetime.date)
    else None
)

json.dumps({'date': datetime.datetime.now()}, default=enco)

Saída: -> {"date": "2015-12-16T04: 48: 20.024609"}


8

Você tem que fornecer uma classe codificador personalizado com o clsparâmetro de json.dumps. Para citar os documentos :

>>> import json
>>> class ComplexEncoder(json.JSONEncoder):
...     def default(self, obj):
...         if isinstance(obj, complex):
...             return [obj.real, obj.imag]
...         return json.JSONEncoder.default(self, obj)
...
>>> dumps(2 + 1j, cls=ComplexEncoder)
'[2.0, 1.0]'
>>> ComplexEncoder().encode(2 + 1j)
'[2.0, 1.0]'
>>> list(ComplexEncoder().iterencode(2 + 1j))
['[', '2.0', ', ', '1.0', ']']

Isso usa números complexos como o exemplo, mas você pode facilmente criar uma classe para codificar datas (exceto que eu acho que o JSON é um pouco confuso sobre datas)


5

A maneira mais simples de fazer isso é alterar a parte do ditado que está no formato de data e hora para isoformato. Esse valor será efetivamente uma string em isoformat com a qual json está bem.

v_dict = version.dict()
v_dict['created_at'] = v_dict['created_at'].isoformat()

5

Na verdade, é bastante simples. Se você precisar serializar datas com frequência, trabalhe com elas como seqüências de caracteres. Você pode convertê-los facilmente novamente como objetos de data e hora, se necessário.

Se você precisar trabalhar principalmente como objetos de data e hora, converta-os como seqüências de caracteres antes de serializar.

import json, datetime

date = str(datetime.datetime.now())
print(json.dumps(date))
"2018-12-01 15:44:34.409085"
print(type(date))
<class 'str'>

datetime_obj = datetime.datetime.strptime(date, '%Y-%m-%d %H:%M:%S.%f')
print(datetime_obj)
2018-12-01 15:44:34.409085
print(type(datetime_obj))
<class 'datetime.datetime'>

Como você pode ver, a saída é a mesma nos dois casos. Somente o tipo é diferente.


3

Se você estiver usando o resultado em uma exibição, certifique-se de retornar uma resposta adequada. De acordo com a API, o jsonify faz o seguinte:

Cria uma resposta com a representação JSON dos argumentos fornecidos com um tipo de aplicativo / json mimetype.

Para imitar esse comportamento com o json.dumps, você precisa adicionar algumas linhas extras de código.

response = make_response(dumps(sample, cls=CustomEncoder))
response.headers['Content-Type'] = 'application/json'
response.headers['mimetype'] = 'application/json'
return response

Você também deve retornar um dict para replicar completamente a resposta do jsonify. Portanto, o arquivo inteiro ficará assim

from flask import make_response
from json import JSONEncoder, dumps


class CustomEncoder(JSONEncoder):
    def default(self, obj):
        if set(['quantize', 'year']).intersection(dir(obj)):
            return str(obj)
        elif hasattr(obj, 'next'):
            return list(obj)
        return JSONEncoder.default(self, obj)

@app.route('/get_reps/', methods=['GET'])
def get_reps():
    sample = ['some text', <datetime object>, 123]
    response = make_response(dumps({'result': sample}, cls=CustomEncoder))
    response.headers['Content-Type'] = 'application/json'
    response.headers['mimetype'] = 'application/json'
    return response

1
A pergunta não tem nada a ver com o balão.
Zoran Pavlovic

2
A questão é sobre python. Minha resposta resolve a questão usando python. O OP não disse se a solução deveria incluir ou excluir determinadas bibliotecas. Também é útil para qualquer pessoa que esteja lendo esta pergunta que queira uma alternativa pymongo.
reubano 10/09

Eles questionam é sobre Python e não sobre Flask. O balão nem sequer é necessário na sua resposta à pergunta, então sugiro que você o remova.
Zoran Pavlovic

3

Tente este com um exemplo para analisá-lo:

#!/usr/bin/env python

import datetime
import json

import dateutil.parser  # pip install python-dateutil


class JSONEncoder(json.JSONEncoder):

    def default(self, obj):
        if isinstance(obj, datetime.datetime):
            return obj.isoformat()
        return super(JSONEncoder, self).default(obj)


def test():
    dts = [
        datetime.datetime.now(),
        datetime.datetime.now(datetime.timezone(-datetime.timedelta(hours=4))),
        datetime.datetime.utcnow(),
        datetime.datetime.now(datetime.timezone.utc),
    ]
    for dt in dts:
        dt_isoformat = json.loads(json.dumps(dt, cls=JSONEncoder))
        dt_parsed = dateutil.parser.parse(dt_isoformat)
        assert dt == dt_parsed
        print(f'{dt}, {dt_isoformat}, {dt_parsed}')
        # 2018-07-22 02:22:42.910637, 2018-07-22T02:22:42.910637, 2018-07-22 02:22:42.910637
        # 2018-07-22 02:22:42.910643-04:00, 2018-07-22T02:22:42.910643-04:00, 2018-07-22 02:22:42.910643-04:00
        # 2018-07-22 06:22:42.910645, 2018-07-22T06:22:42.910645, 2018-07-22 06:22:42.910645
        # 2018-07-22 06:22:42.910646+00:00, 2018-07-22T06:22:42.910646+00:00, 2018-07-22 06:22:42.910646+00:00


if __name__ == '__main__':
    test()

2

Minha solução ...

from datetime import datetime
import json

from pytz import timezone
import pytz


def json_dt_serializer(obj):
    """JSON serializer, by macm.
    """
    rsp = dict()
    if isinstance(obj, datetime):
        rsp['day'] = obj.day
        rsp['hour'] = obj.hour
        rsp['microsecond'] = obj.microsecond
        rsp['minute'] = obj.minute
        rsp['month'] = obj.month
        rsp['second'] = obj.second
        rsp['year'] = obj.year
        rsp['tzinfo'] = str(obj.tzinfo)
        return rsp
    raise TypeError("Type not serializable")


def json_dt_deserialize(obj):
    """JSON deserialize from json_dt_serializer, by macm.
    """
    if isinstance(obj, str):
        obj = json.loads(obj)
    tzone = timezone(obj['tzinfo'])
    tmp_dt = datetime(obj['year'],
                      obj['month'],
                      obj['day'],
                      hour=obj['hour'],
                      minute=obj['minute'],
                      second=obj['second'],
                      microsecond=obj['microsecond'])
    loc_dt = tzone.localize(tmp_dt)
    deserialize = loc_dt.astimezone(tzone)
    return deserialize    

Ok, agora alguns testes.

# Tests
now = datetime.now(pytz.utc)

# Using this solution
rsp = json_dt_serializer(now)
tmp = json_dt_deserialize(rsp)
assert tmp == now
assert isinstance(tmp, datetime) == True
assert isinstance(now, datetime) == True

# using default from json.dumps
tmp = json.dumps(datetime.now(pytz.utc), default=json_dt_serializer)
rsp = json_dt_deserialize(tmp)
assert isinstance(rsp, datetime) == True

# Lets try another timezone
eastern = timezone('US/Eastern')
now = datetime.now(eastern)
rsp = json_dt_serializer(now)
tmp = json_dt_deserialize(rsp)

print(tmp)
# 2015-10-22 09:18:33.169302-04:00

print(now)
# 2015-10-22 09:18:33.169302-04:00

# Wow, Works!
assert tmp == now

2

Aqui está minha solução completa para converter datetime em JSON e vice-versa.

import calendar, datetime, json

def outputJSON(obj):
    """Default JSON serializer."""

    if isinstance(obj, datetime.datetime):
        if obj.utcoffset() is not None:
            obj = obj - obj.utcoffset()

        return obj.strftime('%Y-%m-%d %H:%M:%S.%f')
    return str(obj)

def inputJSON(obj):
    newDic = {}

    for key in obj:
        try:
            if float(key) == int(float(key)):
                newKey = int(key)
            else:
                newKey = float(key)

            newDic[newKey] = obj[key]
            continue
        except ValueError:
            pass

        try:
            newDic[str(key)] = datetime.datetime.strptime(obj[key], '%Y-%m-%d %H:%M:%S.%f')
            continue
        except TypeError:
            pass

        newDic[str(key)] = obj[key]

    return newDic

x = {'Date': datetime.datetime.utcnow(), 34: 89.9, 12.3: 90, 45: 67, 'Extra': 6}

print x

with open('my_dict.json', 'w') as fp:
    json.dump(x, fp, default=outputJSON)

with open('my_dict.json') as f:
    my_dict = json.load(f, object_hook=inputJSON)

print my_dict

Resultado

{'Date': datetime.datetime(2013, 11, 8, 2, 30, 56, 479727), 34: 89.9, 45: 67, 12.3: 90, 'Extra': 6}
{'Date': datetime.datetime(2013, 11, 8, 2, 30, 56, 479727), 34: 89.9, 45: 67, 12.3: 90, 'Extra': 6}

Arquivo JSON

{"Date": "2013-11-08 02:30:56.479727", "34": 89.9, "45": 67, "12.3": 90, "Extra": 6}

Isso me permitiu importar e exportar objetos de strings, ints, floats e datetime. Não deve ser difícil estender para outros tipos.


1
Explode em Python 3 com TypeError: 'str' does not support the buffer interface. É por causa do 'wb'modo aberto, deve ser 'w'. Ele também é deserializado quando temos dados semelhantes ao '0000891618-05-000338'padrão de data semelhante, mas sem correspondência.
Omikron

2

Converta date em string

date = str(datetime.datetime(somedatetimehere)) 

jjmontes resposta faz exatamente isso, mas sem a necessidade de fazê-lo explicitamente para cada data ...
bluesummers

2

Geralmente, existem várias maneiras de serializar datetime, como:

  1. String ISO, curta e pode incluir informações de fuso horário, por exemplo, resposta de @ jgbarah
  2. Registro de data e hora (dados do fuso horário são perdidos), por exemplo, a resposta de @ JayTaylor
  3. Dicionário de propriedades (incluindo fuso horário).

Se você estiver de acordo com a última maneira, o pacote json_tricks manipula datas, horários e horários, incluindo fusos horários.

from datetime import datetime
from json_tricks import dumps
foo = {'title': 'String', 'datetime': datetime(2012, 8, 8, 21, 46, 24, 862000)}
dumps(foo)

que dá:

{"title": "String", "datetime": {"__datetime__": null, "year": 2012, "month": 8, "day": 8, "hour": 21, "minute": 46, "second": 24, "microsecond": 862000}}

Então, tudo que você precisa fazer é

`pip install json_tricks`

e importe de em json_tricksvez de json.

A vantagem de não armazená-lo como uma única string, int ou float ocorre ao decodificar: se você encontrar apenas uma string ou especialmente int ou float, precisará saber algo sobre os dados para saber se é uma data e hora. Como regra, você pode armazenar os metadados para que possam ser decodificados automaticamente, e é json_tricksisso que faz com você. Também é facilmente editável para humanos.

Disclaimer: é feito por mim. Porque eu tive o mesmo problema.


1

Recebi a mesma mensagem de erro ao escrever o decorador de serialização dentro de uma classe com sqlalchemy. Então, em vez de:

Class Puppy(Base):
    ...
    @property
    def serialize(self):
        return { 'id':self.id,
                 'date_birth':self.date_birth,
                  ...
                }

Simplesmente peguei emprestado a ideia de jgbarah de usar isoformat () e acrescentei o valor original com isoformat (), para que agora se pareça com:

                  ...
                 'date_birth':self.date_birth.isoformat(),
                  ...

1

Uma solução rápida, se você quiser sua própria formatação

for key,val in sample.items():
    if isinstance(val, datetime):
        sample[key] = '{:%Y-%m-%d %H:%M:%S}'.format(val) #you can add different formating here
json.dumps(sample)

1

Se você estiver nos dois lados da comunicação, poderá usar as funções repr () e eval () junto com json.

import datetime, json

dt = datetime.datetime.now()
print("This is now: {}".format(dt))

dt1 = json.dumps(repr(dt))
print("This is serialised: {}".format(dt1))

dt2 = json.loads(dt1)
print("This is loaded back from json: {}".format(dt2))

dt3 = eval(dt2)
print("This is the same object as we started: {}".format(dt3))

print("Check if they are equal: {}".format(dt == dt3))

Você não deve importar data e hora como

from datetime import datetime

desde eval vai reclamar. Ou você pode passar datetime como um parâmetro para avaliar. De qualquer forma, isso deve funcionar.


0

Eu encontrei o mesmo problema ao externalizar o objeto de modelo django para despejar como JSON. Aqui está como você pode resolvê-lo.

def externalize(model_obj):
  keys = model_obj._meta.get_all_field_names() 
  data = {}
  for key in keys:
    if key == 'date_time':
      date_time_obj = getattr(model_obj, key)
      data[key] = date_time_obj.strftime("%A %d. %B %Y")
    else:
      data[key] = getattr(model_obj, key)
  return data

0
def j_serial(o):     # self contained
    from datetime import datetime, date
    return str(o).split('.')[0] if isinstance(o, (datetime, date)) else None

Uso do utilitário acima:

import datetime
serial_d = j_serial(datetime.datetime.now())
if serial_d:
    print(serial_d)  # output: 2018-02-28 02:23:15

0

Esse superjson da biblioteca pode fazer isso. E você pode facilmente personalizar o serializador json para seu próprio objeto Python, seguindo estas instruções https://superjson.readthedocs.io/index.html#extend .

O conceito geral é:

seu código precisa localizar o método correto de serialização / desserialização com base no objeto python. Normalmente, o nome completo da classe é um bom identificador.

E então seu método ser / deser deve ser capaz de transformar seu objeto em um objeto serializável Json normal, uma combinação de tipo python genérico, dict, lista, string, int, float. E implemente seu método deser inversamente.


-1

Posso não estar 100% correto, mas esta é a maneira mais simples de serializar

#!/usr/bin/python
import datetime,json

sampledict = {}
sampledict['a'] = "some string"
sampledict['b'] = datetime.datetime.now()

print sampledict   # output : {'a': 'some string', 'b': datetime.datetime(2017, 4, 15, 5, 15, 34, 652996)}

#print json.dumps(sampledict)

'''
output : 

Traceback (most recent call last):
  File "./jsonencodedecode.py", line 10, in <module>
    print json.dumps(sampledict)
  File "/usr/lib/python2.7/json/__init__.py", line 244, in dumps
    return _default_encoder.encode(obj)
  File "/usr/lib/python2.7/json/encoder.py", line 207, in encode
    chunks = self.iterencode(o, _one_shot=True)
  File "/usr/lib/python2.7/json/encoder.py", line 270, in iterencode
    return _iterencode(o, 0)
  File "/usr/lib/python2.7/json/encoder.py", line 184, in default
    raise TypeError(repr(o) + " is not JSON serializable")
TypeError: datetime.datetime(2017, 4, 15, 5, 16, 17, 435706) is not JSON serializable


'''

sampledict['b'] = datetime.datetime.now().strftime("%B %d, %Y %H:%M %p")

afterdump = json.dumps(sampledict)

print afterdump  #output : {"a": "some string", "b": "April 15, 2017 05:18 AM"}

print type(afterdump) #<type 'str'>


afterloads = json.loads(afterdump) 

print afterloads # output : {u'a': u'some string', u'b': u'April 15, 2017 05:18 AM'}


print type(afterloads) # output :<type 'dict'> 
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.