¿Cómo superar "datetime.datetime not JSON serializable"?

Tengo un dict básico de la siguiente manera:

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

Cuando trato de hacer jsonify(sample) Yo obtengo:

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

¿Qué puedo hacer para que la muestra de mi diccionario supere el error anterior?

Nota: Aunque puede no ser relevante, los diccionarios se generan a partir de la recuperación de registros de mongodb donde cuando imprimo str(sample['somedate']), la salida es 2012-08-08 21:46:24.862000.

preguntado el 09 de agosto de 12 a las 03:08

¿Es esto específicamente Python en general, o posiblemente Django? -

Técnicamente es específicamente python, no estoy usando django, sino recuperando registros de mongodb. -

Estoy usando mongoengine, pero si pymongo tiene mejores formas de evitar esto o superarlo, por favor dígalo. -

La pregunta vinculada esencialmente le dice que no intente serializar el objeto de fecha y hora, sino que lo convierta en una cadena en el formato ISO común antes de serializar. -

30 Respuestas

Mi vertedero JSON rápido y sucio que come fechas y todo:

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

default es una función aplicada a objetos que no son serializables.
En este caso es str, por lo que simplemente convierte todo lo que no sabe en cadenas. Lo cual es excelente para la serialización, pero no tanto cuando se deserializa (de ahí el "rápido y sucio"), ya que cualquier cosa podría haber sido encadenada sin previo aviso, por ejemplo, una función o una matriz numpy.

Respondido el 27 de Septiembre de 21 a las 09:09

Esto es increíble, pero desafortunadamente no entendí lo que sucedió. ¿Alguien puede explicar esta respuesta? - Kishor Pawar

@KishorPawar: default es una función aplicada a objetos que no son serializables. en este caso es str, por lo que simplemente convierte todo lo que no sabe en cadenas. Lo cual es excelente para la serialización, pero no tanto cuando se deserializa (de ahí el "rápido y sucio"), ya que cualquier cosa podría haber sido encadenada sin previo aviso, por ejemplo, una función o una matriz numpy. - Marcos

@Mark increíble. Gracias. Útil cuando conoce el tipo de esos valores no serializables como fechas. - Kishor Pawar

@jjmontes, no sirve para todo, por ejemplo json.dumps({():1,type(None):2},default=str) plantea TypeError, no puede tener tipo o tupla. - Alancalvitti

Lo siento despues de este comentario solo encontre que es un error de sintaxis y no de default=str. Gracias por esto - Santhosh

Actualizado para 2018

La respuesta original acomodó la forma en que los campos de "fecha" de MongoDB se representaron como:

{"$date": 1506816000000}

Si desea una solución genérica de Python para serializar datetime a json, echa un vistazo La respuesta de @jjmontes para una solución rápida que no requiere dependencias.


Como está utilizando mongoengine (según los comentarios) y pymongo es una dependencia, pymongo tiene utilidades integradas para ayudar con la serialización json:
http://api.mongodb.org/python/1.10.1/api/bson/json_util.html

Ejemplo de uso (serialización):

from bson import json_util
import json

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

Ejemplo de uso (deserialización):

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

Django

Django proporciona un nativo DjangoJSONEncoder serializador que se ocupa de este tipo de manera adecuada.

Ver 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
)

Una diferencia que he notado entre DjangoJSONEncoder y usando una costumbre default Me gusta esto:

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
)

Es que Django despoja un poco de los datos:

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

Por lo tanto, es posible que deba tener cuidado con eso en algunos casos.

Respondido el 11 de diciembre de 18 a las 20:12

¿Es una buena/mala práctica mezclar varias bibliotecas, es decir, tener mongoengine para insertar documentos y pymongo para consulta/recuperación? - Rolando

No es una mala práctica, solo implica cierta dependencia de las bibliotecas que usa su biblioteca principal. Si no puede lograr lo que necesita de mongoengine, baje a pymongo. Es lo mismo con Django MongoDB. Con el último, intentaría permanecer dentro del ORM de django para mantener un estado agnóstico de back-end. Pero a veces no puede hacer lo que necesita en la abstracción, por lo que despliega una capa. En este caso, no tiene ninguna relación con su problema, ya que solo está utilizando métodos de utilidad para acompañar el formato JSON. - jdi

Estoy probando esto con Flask y parece que al usar json.dump, no puedo poner un envoltorio jsonify() a su alrededor para que regrese en application/json. Intentando devolver jsonify(json.dumps(sample, default=json_util.default)) - Rolando

@amit No se trata tanto de memorizar la sintaxis, sino de aprender a leer documentación y almacenar suficiente información en mi cabeza para reconocer dónde y cuándo necesito recuperarla nuevamente. En este caso, uno podría decir "Oh, un objeto personalizado con json" y luego actualizar rápidamente ese uso: jdi

@guyskk No he rastreado cambios en bjson o mongo desde que escribí esto hace 5 años. Pero si desea controlar la serialización de la fecha y hora, debe escribir su propia función de controlador predeterminada como se ilustra en la respuesta dada por jgbarah: jdi

Sobre la base de otras respuestas, una solución simple basada en un serializador específico que simplemente convierte datetime.datetime y datetime.date objetos a cadenas.

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 se ve, el código solo verifica si el objeto es de clase datetime.datetime or datetime.date, y luego usa .isoformat() para producir una versión serializada del mismo, de acuerdo con el formato ISO 8601, AAAA-MM-DDTHH:MM:SS (que se decodifica fácilmente con JavaScript). Si se buscan representaciones serializadas más complejas, se podría usar otro código en lugar de str() (ver otras respuestas a esta pregunta para ver ejemplos). El código termina lanzando una excepción, para tratar el caso de que se llame con un tipo no serializable.

Esta función json_serial se puede utilizar de la siguiente manera:

from datetime import datetime
from json import dumps

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

Los detalles sobre cómo funciona el parámetro predeterminado para json.dumps se pueden encontrar en Sección Uso básico de la documentación del módulo json.

respondido 09 mar '18, 00:03

sí, la respuesta correcta, más bonita import datetime y if isinstance(obj, datetime.datetime), perdí mucho tiempo porque no usé from datetime import datetime , de todos modos gracias - Sergio

pero esto no explica cómo deserializarlo con el tipo correcto, ¿no? - Trin Azul

No, @BlueTrin, no se dijo nada al respecto. En mi caso, estoy deserializando en JavaScript, que funciona de forma inmediata. - jgbarah

Esto provocará un comportamiento inesperado si el módulo json alguna vez se actualiza para incluir la serialización de objetos de fecha y hora. - Justin

@serg Pero convertir los tiempos a UTC unificaría 01:00:00+01:00 y 02:00:00+00:00 que se supone que no son lo mismo, dependiendo del contexto. Por supuesto, se refieren al mismo punto en el tiempo, pero la compensación podría ser un aspecto relevante del valor. - Alfa

Acabo de encontrar este problema y mi solución es subclase 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)

En su llamada, haga algo como: json.dumps(yourobj, cls=DateTimeEncoder) La .isoformat() Obtuve de una de las respuestas anteriores.

respondido 21 nov., 14:09

actualizado porque implementar un JSONEncoder personalizado debería ser la forma correcta de hacerlo: 3k-

Esta no solo debería ser la respuesta principal, sino que debería ser parte del codificador json normal. Si tan solo la decodificación fuera menos ambigua... - Joost

Para aquellos que usan Django, vea DjangoJSONEncoder. docs.djangoproject.com/en/dev/topics/serialization/… - s kirby

Súper útil. La última línea podría ser return super(DateTimeEncoder, self).default(o) - bob stein

Con Python 3, la última línea es aún más simple: return super().default(o) - ariddell

si está usando python3.7, entonces la mejor solución es usar datetime.isoformat() y datetime.fromisoformat(); trabajan con ingenuos y conscientes datetime objetos:

#!/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() }
    raise TypeError('...')

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))

salida:

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

si está utilizando python3.6 o inferior, y solo le importa el valor del tiempo (no la zona horaria), entonces puede usar datetime.timestamp() y datetime.fromtimestamp() en lugar de;

si está utilizando python3.6 o inferior, y le importa la zona horaria, puede obtenerlo a través de datetime.tzinfo, pero debe serializar este campo usted mismo; la forma más fácil de hacer esto es agregar otro campo _tzinfo en el objeto serializado;

finalmente, tenga cuidado con las precisiones en todos estos ejemplos;

Respondido 28 Jul 21, 19:07

datetime.isoformat() también está presente en Python 2.7: docs.python.org/2/library/… - powlo

Solución muy útil. ¿Podría también apoyar datetime.time? - idbrii

@idbrii Como dijo @egvo, te olvidaste default=default en su edición, que introdujo el error. Pero tienes razón super().default(obj) es inútil aquí. De hecho, provino de un ejemplo de json-encoder donde es útil. - Cyker

Aquí está mi solución:

import json


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

Entonces puedes usarlo así:

json.dumps(dictionnary, cls=DatetimeEncoder)

Respondido el 01 de Septiembre de 20 a las 13:09

aceptar. Mucho mejor, al menos fuera del contexto mongodb. Tu puedes hacer isinstance(obj, datetime.datetime) dentro del TypeError, agregue más tipos para manejar y termine con el str(obj) or repr(obj). Y todos sus volcados pueden apuntar a esta clase especializada. - JL Peyret

@Natim esta solución es la mejor. +1 - Rayo Souvik

¿Qué pasa con la decodificación? - Tomás Sauvajón

Convertir la fecha en una cadena

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

Respondido 08 Abr '18, 14:04

¿Y cómo podría deserializarlo en Python? - wobmene

El problema es si tiene muchos objetos de fecha y hora incrustados profundamente en una estructura de datos, o si son aleatorios. Este no es un método confiable. - rebotes

para deserializar: oDate = datetime.datetime.strptime(sDate, '%Y-%m-%d %H:%M:%S.%f'). Formatos obtenidos de: docs.python.org/2/library/datetime.html - novela

Votado negativo porque ignora la información de la zona horaria. Manten eso en mente .now() utiliza la hora local, sin indicarlo. Al menos .utcnow() debe usarse (y luego agregar +0000 o Z) - Daniel F

@DanielF At least .utcnow() should be used No exactamente, datetime.now(timezone.utc) se recomienda, consulte la advertencia en: docs.python.org/3.8/library/…. - Toreno96

Deberías aplicar .strftime() método en .datetime.now() método para hacerlo como un serializable método.

He aquí un ejemplo:

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

Salida:

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

Respondido el 20 de junio de 21 a las 08:06

El método json.dumps puede aceptar un parámetro opcional llamado predeterminado que se espera que sea una función. Cada vez que JSON intenta convertir un valor, no sabe cómo convertirlo, llamará a la función que le pasamos. La función recibirá el objeto en cuestión y se espera que devuelva la representación JSON del objeto.

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

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

Respondido 31 Jul 18, 22:07

Para otros que no necesitan o no quieren usar la biblioteca pymongo para esto... pueden lograr la conversión JSON de fecha y hora fácilmente con este pequeño fragmento:

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,))

Entonces úsalo así:

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

salida: 

'1365091796124'

Respondido el 05 de enero de 16 a las 13:01

No deberia millis= tener sangría dentro de la instrucción if? Probablemente también sea mejor usar str(obj) para obtener el formato ISO, que creo que es más común. - rebotes

¿Por qué quieres que esté sangrado? Este fragmento funciona y la salida resultante se puede deserializar/analizar fácilmente desde javascript. - Jay Taylor

Porque obj puede no ser un objeto [hora, fecha, fecha y hora] - rebotes

su ejemplo es incorrecto si la zona horaria local tiene un desplazamiento UTC distinto de cero (la mayoría de ellos). datetime.now() devuelve la hora local (como un objeto ingenuo de fecha y hora) pero su código asume que obj está en UTC si no reconoce la zona horaria. Usar datetime.utcnow() en lugar de. - jfs

Se ajustó para generar un error de tipo si obj no se reconoce según la recomendación de la documentación de Python en docs.python.org/2/library/json.html#basic-usage. - Jay Taylor

Mi solución (con menos verbosidad, creo):

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)

Entonces usa jsondumps en lugar de json.dumps. Se imprimirá:

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

Si lo desea, luego puede agregar otros casos especiales a este con un simple giro del default método. Ejemplo:

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)

Respondido 31 Jul 13, 01:07

Debe usar isinstance(o, (datetime.date, datetime.datetime,)). Probablemente no estaría de más incluir datetime.time también. - rebotes

Ya no creo que sea una buena solución. Probablemente las conversiones deberían ocupar un lugar más privilegiado, y también un lugar más comprensible, en su código, para que sepa a qué se está convirtiendo cuando coloca cosas en una base de datos, o lo que sea, en lugar de tener todo hecho por un función transparente. Pero no sé. - fiatjaf

JSON es bueno para serializar datos para su procesamiento posterior. Es posible que no sepa exactamente cuáles son esos datos. Y no deberías necesitarlo. La serialización de JSON debería funcionar. Al igual que convertir unicode a ascii debería. La incapacidad de Python para hacer esto sin funciones oscuras hace que su uso sea molesto. La validación de la base de datos es un tema aparte en mi opinión. - rebotes

No, no debería "simplemente funcionar". Si no sabe cómo ocurrió la serialización y tiene que acceder a los datos más tarde desde otro programa/lenguaje, entonces está perdido. - fiatjaf

JSON se usa comúnmente para cadenas, enteros, flotantes, fechas (estoy seguro de que otros también usan moneda, temperaturas, comúnmente). Pero datetime es parte de la biblioteca estándar y debería admitir la deserialización. Si no fuera por esta pregunta, todavía estaría buscando manualmente mis blobs json increíblemente complejos (para los cuales no siempre creé la estructura) para fechas y serializándolos 1 por 1. - rebotes

Esta Q se repite una y otra vez: una forma sencilla de parchear el módulo json de modo que la serialización admita la fecha y la hora.

import json
import datetime

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

Luego use la serialización json como siempre lo hace, esta vez con la fecha y hora serializada como isoformato.

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

Resultando en: '{"creado": "2015-08-26T14:21:31.853855"}'

Vea más detalles y algunas palabras de advertencia en: StackOverflow: fecha y hora JSON entre Python y JavaScript

contestado el 23 de mayo de 17 a las 13:05

Parche de mono FTW. Lo desagradable es, por supuesto, que esto modifica el comportamiento del módulo json en toda su aplicación, lo que puede sorprender a otros en una aplicación grande, por lo que generalmente debe usarse con cuidado en mi humilde opinión. - Jaap Versteegh

En realidad es bastante simple. Si necesita serializar fechas con frecuencia, trabaje con ellas como cadenas. Puede volver a convertirlos fácilmente como objetos de fecha y hora si es necesario.

Si necesita trabajar principalmente como objetos de fecha y hora, conviértalos como cadenas 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 puede ver, la salida es la misma en ambos casos. Sólo el tipo es diferente.

Respondido el 01 de diciembre de 18 a las 14:12

Pruebe este con un ejemplo para analizarlo:

#!/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()

Respondido 22 Jul 18, 07:07

Debe proporcionar una clase de codificador personalizado con el cls parámetro de json.dumps. Para citar del 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', ']']

Esto usa números complejos como ejemplo, pero puede crear fácilmente una clase para codificar fechas (excepto que creo que JSON es un poco confuso con las fechas)

Respondido 09 ago 12, 04:08

La forma más sencilla de hacer esto es cambiar la parte del dictado que está en formato de fecha y hora a formato iso. Ese valor será efectivamente una cadena en formato iso con el que json está bien.

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

Respondido el 30 de junio de 15 a las 19:06

Según la respuesta de @jjmontes, he usado el siguiente enfoque. Para usuarios de petacas y de petacas

# get json string
jsonStr = json.dumps(my_dictionary, indent=1, sort_keys=True, default=str)
# then covert json string to json object
return json.loads(jsonStr)

contestado el 02 de mayo de 21 a las 07:05

Esto no es distinto de la Respuesta de jjmontes; en su lugar, debería ser un comentario sobre su Respuesta. - cellepo

Aquí hay una solución simple para superar el problema de "datetime not 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)

Salida:-> {"date": "2015-12-16T04:48:20.024609"}

Respondido el 16 de diciembre de 15 a las 03:12

Tengo una aplicación con un problema similar; mi enfoque fue JSONize el valor de fecha y hora como una lista de 6 elementos (año, mes, día, hora, minutos, segundos); podría ir a microsegundos como una lista de 7 elementos, pero no tuve necesidad de:

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)

produce:

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

Respondido 02 ago 13, 00:08

No funciona si el tiempo guardado se guarda haciendo datetime.utcnow() - Saurshaz

¿Qué error ve con datetime.utcnow()? Funciona bien para mí. - codificación

Mi solución ...

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    

Bien, ahora algunas pruebas.

# 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

Respondido 22 Oct 15, 14:10

En general, hay varias formas de serializar fechas y horas, como:

  1. Cadena ISO, corta y puede incluir información de la zona horaria, por ejemplo, @jgbarah's https://www.youtube.com/watch?v=xB-eutXNUMXJtA&feature=youtu.be
  2. Marca de tiempo (se pierden los datos de la zona horaria), por ejemplo, @JayTaylor's https://www.youtube.com/watch?v=xB-eutXNUMXJtA&feature=youtu.be
  3. Diccionario de propiedades (incluida la zona horaria).

Si está de acuerdo con la última forma, el trucos_json el paquete maneja fechas, horas y fechas y horas, incluidas las zonas horarias.

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

lo que da:

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

Así que todo lo que necesitas hacer es

`pip install json_tricks`

y luego importar desde json_tricks en lugar de json.

La ventaja de no almacenarlo como una sola cadena, int o float surge al decodificar: si encuentra solo una cadena o especialmente int o float, necesita saber algo sobre los datos para saber si es una fecha y hora. Como dictado, puede almacenar metadatos para que puedan ser decodificados automáticamente, que es lo que json_tricks hace por ti También es fácilmente editable para humanos.

Descargo de responsabilidad: está hecho por mí. Porque yo tenía el mismo problema.

contestado el 23 de mayo de 17 a las 13:05

Si está en ambos lados de la comunicación, puede usar repr () y eval () funciona junto con 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))

No debe importar fecha y hora como

from datetime import datetime

ya que eval se quejará. O puede pasar datetime como parámetro para evaluar. En cualquier caso, esto debería funcionar.

Respondido 08 ago 18, 13:08

Convertir el date a string

date = str(datetime.datetime(somedatetimehere)) 

Respondido 01 Feb 16, 06:02

La respuesta de jjmontes hace exactamente eso, pero sin la necesidad de hacerlo explícitamente para cada fecha... - veranosazules

Aquí está mi solución completa para convertir datetime a JSON y viceversa.

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

Salida

{'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}

Archivo JSON

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

Esto me ha permitido importar y exportar cadenas, enteros, flotantes y objetos de fecha y hora. No debería ser demasiado difícil de extender para otros tipos.

Respondido el 12 de enero de 16 a las 04:01

Explota en Python 3 con TypeError: 'str' does not support the buffer interface. es por 'wb' modo abierto, debe ser 'w'. También sopla en la deserialización cuando tenemos datos similares a la fecha como '0000891618-05-000338' pero no coincide con el patrón. - omikron

Si está utilizando el resultado en una vista, asegúrese de devolver una respuesta adecuada. Según la API, jsonify hace lo siguiente:

Crea una respuesta con la representación JSON de los argumentos proporcionados con un tipo MIME application/json.

Para imitar este comportamiento con json.dumps, debe agregar algunas líneas adicionales de código.

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

También debe devolver un dictado para replicar completamente la respuesta de jsonify. Entonces, todo el archivo se verá así

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

Respondido el 09 de junio de 13 a las 14:06

La pregunta no tiene nada que ver con el matraz. - zoran pavlovic

La pregunta es sobre Python. Mi respuesta resuelve la pregunta usando python. El OP no dijo si la solución debería incluir o excluir ciertas bibliotecas. También es útil para cualquier otra persona que lea esta pregunta y quiera una alternativa a pymongo. - rubano

La pregunta es sobre Python y no sobre Frasco. Flask ni siquiera es necesario en su respuesta a la pregunta, por lo que le sugiero que lo elimine. - zoran pavlovic

En matraz es mucho más fácil de usar. flask.json.dumps maneja objetos de fecha y hora. - Jonatan

Puede que no sea 100% correcto, pero esta es la forma más sencilla 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'> 

Respondido 15 Abr '17, 09:04

Recibí el mismo mensaje de error al escribir el decorador de serialización dentro de una Clase con sqlalchemy. Así que en lugar de:

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

Simplemente tomé prestada la idea de jgbarah de usar isoformat() y agregué el valor original con isoformat(), de modo que ahora se ve así:

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

Respondido el 02 de diciembre de 15 a las 22:12

Una solución rápida si desea su propio formato

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)

contestado el 24 de mayo de 16 a las 14:05

Me encontré con el mismo problema al externalizar el objeto del modelo django para volcarlo como JSON. Así es como puedes resolverlo.

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

Respondido 20 Jul 13, 18:07

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

Uso de la utilidad anterior:

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

Respondido 27 Feb 18, 20:02

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.