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

973

Tengo un dictado básico de la siguiente manera:

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

Cuando trato de hacerlo jsonify(sample), obtengo:

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

¿Qué puedo hacer para que mi muestra de diccionario pueda superar el error anterior?

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

10
  • 1
    ¿Es esto específicamente python en general, o posiblemente django? jdi 9 de agosto de 2012 a las 2:05
  • 2
    Técnicamente es específicamente python, no estoy usando django, pero recupero registros de mongodb. Rolando 9 de agosto de 2012 a las 2:05
  • posible duplicado de JSON datetime entre Python y JavaScriptjdi 9 de agosto de 2012 a las 2:05
  • Estoy usando mongoengine, pero si pymongo tiene mejores formas de solucionar esto o superarlo, dígalo. Rolando 9 de agosto de 2012 a las 2:07
  • 3
    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 serializarlo. Thomas Kelley 9 de agosto de 2012 a las 2:13
483

Actualizado para 2018

La respuesta original acomodaba la forma en que los campos de "fecha" de MongoDB se representaban como:

{"$date": 1506816000000}

Si desea una solución genérica de Python para serializar datetimea json, consulte la respuesta de @jjmontes para obtener una solución rápida que no requiera dependencias.


Como está usando mongoengine (por 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

Uso de ejemplo (serialización):

from bson import json_util
import json

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

Uso de ejemplo (deserialización):

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

Django

Django proporciona un DjangoJSONEncoderserializador nativo que se ocupa de este tipo de forma 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 DjangoJSONEncodery el uso de una costumbre defaultcomo esta:

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 quita 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.

20
  • 4
    ¿Es una buena / mala práctica mezclar varias bibliotecas, es decir, tener mongoengine para insertar documentos y pymongo para consultas / recuperación? Rolando 9 de agosto de 2012 a las 2:25
  • No es una mala práctica, solo implica cierta dependencia de las bibliotecas que utiliza su biblioteca principal. Si no puede lograr lo que necesita de mongoengine, desplácese hacia pymongo. Es lo mismo con Django MongoDB. Con el último, intentaría permanecer dentro del ORM de django para mantener el estado agnóstico del backend. Pero a veces no puede hacer lo que necesita en la abstracción, por lo que despliega una capa. En este caso, no tiene nada que ver con su problema, ya que solo está utilizando métodos de utilidad para acompañar el formato JSON. jdi 9 de agosto de 2012 a las 2:29
  • Estoy probando esto con Flask y parece que al usar json.dump, no puedo poner un contenedor jsonify () alrededor de él de modo que regrese en application / json. Intentando devolver jsonify (json.dumps (muestra, predeterminado = json_util.default))Rolando 9 de agosto de 2012 a las 2:39
  • 3
    @amit No se trata tanto de memorizar la sintaxis, sino de mejorar la lectura de documentación y almacenar suficiente información en mi cabeza para reconocer dónde y cuándo necesito recuperarla. En este caso, uno podría decir "Oh, un objeto personalizado con json" y luego actualizar rápidamente ese usojdi 2/07/2015 a las 19:12
  • 2
    @guyskk No he seguido los 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 jgbarahjdi 7 de nov. De 2017 a las 10:24
982

Mi volcado JSON rápido y sucio que come dátiles y todo:

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

defaultes una función aplicada a objetos que no son serializables.
En este caso lo es str, por lo que simplemente convierte todo lo que no conoce en cadenas. Lo cual es genial 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.

17
  • 18
    Esto es asombroso, pero desafortunadamente no entendí lo que sucedió. ¿Alguien puede explicar esta respuesta? Kishor Pawar 4 de octubre de 2016 a las 5:31
  • 91
    @KishorPawar: defaultes una función aplicada a objetos que no son serializables. En este caso lo es str, por lo que simplemente convierte todo lo que no conoce en cadenas. Lo cual es genial 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. Mark 19/10/2016 a las 20:54
  • 2
    @Mark impresionante. Gracias. Útil cuando conoce el tipo de esos valores no serializables como las fechas. Kishor Pawar 20/10/2016 a las 0:32
  • 25
    ¿Por qué pasé toda mi vida sin saber esto? :)Arel 3 de mayo de 2019 a las 15:24
  • 2
    @jjmontes, no funciona para todo, por ejemplo , json.dumps({():1,type(None):2},default=str)aumenta TypeError, no puede tener tipo o tupla. alancalvitti 22/08/19 a las 18:46
493

Sobre la base de otras respuestas, una solución simple basada en un serializador específico que solo convierte datetime.datetimey datetime.dateobjetos en 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.datetimeo datetime.date, y luego se usa .isoformat()para producir una versión serializada del mismo, de acuerdo con el formato ISO 8601, YYYY-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 () (consulte otras respuestas a esta pregunta para ver ejemplos). El código termina generando 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 de json.dumps se pueden encontrar en la Sección Uso básico de la documentación del módulo json .

9
  • 6
    sí, la respuesta correcta, más bonita import datetime y si es una instancia (obj, datetime.datetime), perdí mucho tiempo porque no se usa desde datetime import datetime, de todos modos graciasSérgio 22 de septiembre de 2014 a las 20:43
  • dieciséis
    pero esto no explica cómo deserializarlo con el tipo correcto, ¿no es así? BlueTrin 26/06/15 a las 15:53
  • 3
    No, @BlueTrin, no se dijo nada al respecto. En mi caso, estoy deserializando en JavaScript, que funciona de forma inmediata. jgbarah 28 de junio de 2015 a las 8:09
  • 1
    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 16/08/16 a las 17:27
  • 1
    @serg Pero convertir los tiempos a UTC unificaría 01:00:00+01:00y 02:00:00+00:00no se supone que sean los mismos, según el contexto. Por supuesto, se refieren al mismo punto en el tiempo, pero la compensación puede ser un aspecto relevante del valor. Alfe 18 de septiembre de 2019 a las 12:26
243

Acabo de encontrarme con 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)El .isoformat()obtuve de una de las respuestas anteriores.

11
  • 23
    mejorado porque la implementación de un JSONEncoder personalizado debería ser el camino correcto a seguir3k- 29/08/15 a las 22:13
  • 27
    Esta no solo debería ser la respuesta principal, sino que debería ser parte del codificador json normal. Si tan sólo la decodificación fue menos ambiguo ..Joost 7 oct 2015 a las 15:00
  • 6
    Para aquellos que usan Django, consulte DjangoJSONEncoder. docs.djangoproject.com/en/dev/topics/serialization/…S. Kirby 17 de noviembre de 2016 a las 7:06
  • 4
    Muy útil. La última línea podría serreturn super(DateTimeEncoder, self).default(o)Bob Stein 28 de enero de 2017 a las 1:48
  • 18
    Con Python 3, la última línea es aún más simple:return super().default(o)ariddell 15/01/18 a las 13:20
135

Convierte la fecha en una cadena

sample['somedate'] = str( datetime.utcnow() )
5
  • 11
    ¿Y cómo podría deserializarlo en Python? wobmene 27 de marzo de 2014 a las 12:58
  • 69
    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. Rebs 10 de mayo de 2014 a las 6:37
  • 3
    deserializar: oDate = datetime.datetime.strptime(sDate, '%Y-%m-%d %H:%M:%S.%f'). Formatos obtenidos de: docs.python.org/2/library/datetime.htmlRoman 11 de mayo de 2015 a las 10:45
  • 14
    Votado en contra, ya que ignora la información de la zona horaria. Tenga en cuenta que .now()usa la hora local, sin indicar esto. Al menos se .utcnow()debe usar (y luego se agrega un +0000 o Z)Daniel F 25/08/15 a las 20:06
  • 2
    @DanielF At least .utcnow() should be usedNo exactamente, datetime.now(timezone.utc)se recomienda, consulte la advertencia en: docs.python.org/3.8/library/… . Toreno96 19 de mayo de 2020 a las 10:04
82

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)

producción: 

'1365091796124'
5
  • 1
    ¿No debería millis=tener sangría dentro de la declaración if? También es probablemente mejor usar str (obj) para obtener el formato ISO que creo que es más común. Rebs 10 de mayo de 2014 a las 6:45
  • ¿Por qué querrías que tuviera sangría? Este fragmento funciona y la salida resultante se puede deserializar / analizar fácilmente desde javascript. Jay Taylor 12 de mayo de 2014 a las 0:29
  • 5
    Dado que es posible que obj no sea un objeto [time, date, datetime]Rebs 13 de mayo '14 a las 14:30 h.
  • 2
    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 de fecha y hora ingenuo) pero su código asume que objestá en UTC si no es consciente de la zona horaria. Úselo en su datetime.utcnow()lugar. jfs 12/10/2014 a las 23:25
  • 1
    Se ajustó para generar un error de tipo si obj no se reconoce según la recomendación de documentación de Python en docs.python.org/2/library/json.html#basic-usage . Jay Taylor 5/11/15 a las 18:27
60

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)
4
  • 1
    de acuerdo. Mucho mejor, al menos fuera del contexto de mongodb. Puede hacerlo isinstance(obj, datetime.datetime)dentro de TypeError, agregar más tipos para manejar y terminar con str(obj)o repr(obj). Y todos sus volcados solo pueden apuntar a esta clase especializada. JL Peyret 11/07/2017 a las 21:54
  • @Natim esta solución es la mejor. +1Souvik Ray 22/11/19 a las 19:34
  • ¿Y la decodificación? Thomas Sauvajon 17 jul.20 a las 0:53
  • @ThomasSauvajon stackoverflow.com/a/40489783/186202Natim 17/07/20 a las 8:16
30

si está usando python3.7, entonces la mejor solución es usar datetime.isoformat()y datetime.fromisoformat(); trabajan con datetimeobjetos ingenuos y 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() }
    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))

producción:

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

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

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 sencilla de hacer esto es agregar otro campo _tzinfoen el objeto serializado;

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

3
  • datetime.isoformat () también está presente en Python 2.7: docs.python.org/2/library/…powlo 10/06/19 a las 8:56
  • Solución muy útil. ¿Podría también apoyar datetime.time? idbrii 17 de julio a las 23:16
  • 1
    @idbrii Como dijo @egvo, lo olvidó default=defaulten su edición, lo que introdujo el error. Pero tienes razón, super().default(obj)aquí es inútil. De hecho, provino de un ejemplo de codificador json donde es útil. Cyker 28 de julio a las 18:33
22

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 que no sabe cómo convertir, 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)) 
0
21

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 tenía 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"}
2
  • No funciona si el tiempo ahorrado se guarda haciendo datetime.utcnow ()saurshaz 5 de septiembre de 2013 a las 6:34
  • 1
    ¿Qué error está viendo con datetime.utcnow ()? Funciona bien para mi. codingatty 10 de diciembre de 2013 a las 1:54
20

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)

Luego use en jsondumpslugar de json.dumps. Se imprimirá:

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

Si quieres, luego puedes agregar otros casos especiales a esto con un simple giro del defaultmé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)
7
  • 1
    Debe usar isinstance (o, (datetime.date, datetime.datetime,)). Probablemente no estaría de más incluir datetime.time también. Rebs 10 de mayo de 2014 a las 6:56
  • Ya no creo que esta 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 las cosas en una base de datos, o lo que sea, en lugar de que todo lo haga un función transparente. Pero no lo se. fiatjaf 10 de mayo de 2014 a las 11:31
  • 2
    JSON es bueno para serializar datos para procesarlos más tarde. Es posible que no sepa exactamente cuáles son esos datos. Y no debería ser necesario. Serializar JSON debería funcionar. Al igual que debería convertir unicode a ascii. 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 separado en mi opinión. Rebs 11 de mayo de 2014 a las 11:52
  • 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 / idioma, entonces está perdido. fiatjaf 12 de mayo de 2014 a las 1:45
  • 2
    JSON se usa comúnmente para cadenas, ints, flotadores, fechas (estoy seguro de que otros usan moneda, temperaturas, comúnmente también). 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 que no siempre creé la estructura) para las fechas y serializándolos 1 por 1.Rebs 13 de mayo de 2014 a las 3:11
18

Esta Q se repite una y otra vez, una forma sencilla de parchear el módulo json para que la serialización admita datetime.

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: '{"created": "2015-08-26T14: 21: 31.853855"}'

Vea más detalles y algunas palabras de precaución en: StackOverflow: JSON datetime entre Python y JavaScript

1
  • Parche de mono FTW. Lo malo 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 10 feb 2019 a las 19:16
15

Debe aplicar .strftime()método en .datetime.now()método para convertirlo en un método serializable .

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

Producción:

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

Debe proporcionar una clase de codificador personalizado con el clsparámetro de json.dumps. Para citar de los 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 sobre las fechas)

11

Aquí hay una solución simple para superar el problema de "fecha y hora no serializable JSON".

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"}

6

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

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

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()
0
6

De hecho, es bastante sencillo. Si necesita serializar fechas con frecuencia, trabaje con ellas como cadenas. Puede 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. Solo el tipo es diferente.

2

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

Creates a Response with the JSON representation of the given arguments with an application/json mimetype.

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 dict 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
4
  • 1
    La pregunta no tiene nada que ver con el matraz. Zoran Pavlovic 9 de septiembre de 2014 a las 21:16
  • 3
    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. reubano 10 de septiembre de 2014 a las 7:24
  • Se pregunta es sobre Python y no sobre Flask. El frasco ni siquiera es necesario en su respuesta a la pregunta, por lo que le sugiero que lo retire. Zoran Pavlovic 10 de septiembre de 2014 a las 10:37
  • En matraz es mucho más fácil de usar flask.json.dumps, maneja objetos de fecha y hora. Jonatan 31 jul.20 a las 10:10
2

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    

Ok, 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
2

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

Producción

{'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, entradas, flotantes y objetos de fecha y hora. No debería ser difícil extenderlo para otros tipos.

1
  • 1
    Explota en Python 3 con TypeError: 'str' does not support the buffer interface. Es debido 'wb'al modo abierto, debería serlo 'w'. También sopla en la deserialización cuando tenemos datos similares a la fecha '0000891618-05-000338'pero que no coinciden con el patrón. omikron 14 feb 14 a las 16:14
2

Convierta el date a string

date = str(datetime.datetime(somedatetimehere)) 
1
  • La respuesta de jjmontes hace exactamente eso, pero sin la necesidad de hacerlo explícitamente para cada fecha ...bluesummers 17 de agosto de 2017 a las 11:34
2

Generalmente, hay varias formas de serializar fechas y horas, como:

  1. Cadena ISO, corta y puede incluir información de zona horaria, por ejemplo, la respuesta de @ jgbarah
  2. Marca de tiempo (se pierden los datos de la zona horaria), por ejemplo, la respuesta de @ JayTaylor
  3. Diccionario de propiedades (incluida la zona horaria).

Si está de acuerdo con la última forma, el paquete json_tricks maneja fechas, horas y fechas, 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}}

Entonces todo lo que necesitas hacer es

`pip install json_tricks`

y luego importar desde en json_trickslugar de json.

La ventaja de no almacenarlo como una sola cadena, int o float viene 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 un dictado, puede almacenar metadatos para que se puedan decodificar automáticamente, que es lo que json_trickshace por usted. También es fácilmente editable para humanos.

Descargo de responsabilidad: está hecho por mí. Porque tuve el mismo problema.

2

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

# 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)
2
  • 1
    ¡Este es un buen truco! Gracias. Maged Saeed 6 de junio a las 12:17
  • 3
    Esto no es distinto de la Respuesta de jjmontes; en su lugar, debería ser un comentario sobre su Respuesta. cellepo 10 de agosto a las 0:38
1

Recibí el mismo mensaje de error al escribir el decorador de serialización dentro de una clase con sqlalchemy. Entonces 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(),
                  ...
1

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

Si está en ambos lados de la comunicación, puede usar las funciones repr () y eval () 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 deberías importar datetime como

from datetime import datetime

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

0

Encontré el mismo problema al externalizar el objeto del modelo django para volcarlo como JSON. Así es como puede 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
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 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
0
0

Esta biblioteca superjson puede hacerlo. Y puede personalizar fácilmente el serializador json para su propio objeto Python siguiendo esta instrucción https://superjson.readthedocs.io/index.html#extend .

El concepto general es:

su código necesita ubicar el método correcto de serialización / deserialización basado en el objeto python. Por lo general, el nombre de clase completo es un buen identificador.

Y luego su método ser / deser debería poder transformar su objeto en un objeto serializable Json regular, una combinación de tipo python genérico, dict, list, string, int, float. E implemente su método deser al revés.