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

743

Tengo un dict básico de la siguiente manera:

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

Cuando trato de hacer jsonify(sample)me sale:

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 no ser relevante, los diccionarios se generan a partir de la recuperación de registros de mongodbdonde imprimo str(sample['somedate']), el resultado es 2012-08-08 21:46:24.862000.

Rolando
fuente
1
¿Es esto específicamente python en general, o posiblemente django?
jdi
1
Técnicamente es específicamente python, no estoy usando django, pero estoy recuperando registros de mongodb.
Rolando
Estoy usando mongoengine, pero si pymongo tiene mejores formas de evitar esto o superar esto, por favor dígalo.
Rolando
3
La pregunta vinculada es esencialmente decirle 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.
Thomas Kelley

Respuestas:

377

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

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 DjangoJSONEncoderserializador nativo 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 noté entre DjangoJSONEncoderusar 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 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 tenga que tener cuidado con eso en algunos casos.

jdi
fuente
3
¿Es una buena / mala práctica mezclar múltiples 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 utiliza su biblioteca principal. Si no puede lograr lo que necesita de mongoengine, vaya a pymongo. Es lo mismo con Django MongoDB. Con la versión posterior, 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 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 colocar un contenedor jsonify () de modo que regrese en application / json. Intentando devolver jsonify (json.dumps (sample, default = json_util.default))
Rolando
2
@amit No se trata tanto de memorizar la sintaxis, sino de mejorar la lectura de la 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
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 jgbarah
jdi
619

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

json.dumps(my_dictionary, indent=4, sort_keys=True, default=str)
jjmontes
fuente
14
Esto es asombroso, pero desafortunadamente no entendí lo que pasó. ¿Alguien puede explicar esta respuesta?
Kishor Pawar
63
@KishorPawar: defaultes una función aplicada a objetos que no son serializables. En este caso lo es str, por lo que solo convierte todo lo que no sabe en cadenas. Lo cual es excelente para la serialización, pero no tan bueno 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 matriz numpy.
Mark
1
@ Mark impresionante. Gracias. Útil cuando conoce el tipo de esos valores no serializables como las fechas.
Kishor Pawar
2
¿Por qué fui toda mi vida sin saber esto? :)
Arel
1
@jjmontes, no funciona para todo, por ejemplo , json.dumps({():1,type(None):2},default=str)aumentos TypeError, no puede tener tipografía o tupla.
alancalvitti
443

Sobre la base de otras respuestas, una solución simple basada en un serializador específico que simplemente 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 comprueba si el objeto es de clase datetime.datetimeo datetime.date, y luego lo utiliza .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 por JavaScript ) Si se buscan representaciones serializadas más complejas, se podría usar otro código en lugar de str () (vea otras respuestas a esta pregunta para ver ejemplos). El código termina con una excepción, para tratar el caso en que se llama con un tipo no serializable.

Esta función json_serial se puede usar 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 la Sección Uso básico de la documentación del módulo json .

jgbarah
fuente
55
Sí, la respuesta correcta, más bonita fecha y hora de importación y si es instancia (obj, datetime.datetime), perdí mucho tiempo porque no utilicé desde datetime import datetime, de todos modos gracias
Sérgio
12
pero esto no explica cómo deserializarlo con el tipo correcto, ¿no?
BlueTrin
2
No, @BlueTrin, no se dijo nada al respecto. En mi caso, estoy deserializando en JavaScript, que funciona de inmediato.
jgbarah
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
1
@serg Pero la conversión de los tiempos a UTC se unificaría 01:00:00+01:00y 02:00:00+00:00no se supone que sean iguales, según el contexto. Se refieren al mismo punto en el tiempo, por supuesto, pero la compensación podría ser un aspecto relevante del valor.
Alfe
211

Acabo de encontrar este problema y mi solución es subclasificar 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)Lo .isoformat()que obtuve de una de las respuestas anteriores.

lenny
fuente
22
mejorado porque implementar un JSONEncoder personalizado debería ser la forma correcta de hacerlo
3k-
25
Esta no solo debería ser la respuesta principal, sino que debería ser parte del codificador json normal. Si solo la decodificación fuera menos ambigua ..
Joost
44
Para aquellos que usan Django, ver DjangoJSONEncoder. docs.djangoproject.com/en/dev/topics/serialization/…
S. Kirby
44
Súper servicial La última línea podría serreturn super(DateTimeEncoder, self).default(o)
Bob Stein el
16
Con Python 3 la última línea es aún más simple:return super().default(o)
ariddell
124

Convierte la fecha en una cadena

sample['somedate'] = str( datetime.utcnow() )
DA
fuente
10
¿Y cómo podría deserializarlo en Python?
wobmene
62
El problema es si tiene muchos objetos de fecha y hora incrustados profundamente en una estructura de datos, o son aleatorios. Este no es un método confiable.
Rebs
3
deserializar: oDate = datetime.datetime.strptime(sDate, '%Y-%m-%d %H:%M:%S.%f'). Formatos obtenidos de: docs.python.org/2/library/datetime.html
Romano
13
Vota abajo ya que ignora la información de la zona horaria. Tenga en cuenta que .now()utiliza la hora local, sin indicar esto. Al menos se .utcnow()debe usar (y luego se agrega un +0000 o Z)
Daniel F
1
@DanielF At least .utcnow() should be usedNo exactamente, datetime.now(timezone.utc)se recomienda, vea la advertencia en: docs.python.org/3.8/library/… .
Toreno96
79

Para otros que no necesitan o quieren usar la biblioteca pymongo para esto ... puede 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,))

Luego úsalo así:

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

salida: 

'1365091796124'
Jay Taylor
fuente
1
¿No se millis=debe sangrar 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
¿Por qué querrías que se sangrara? Este fragmento funciona y la salida resultante se puede deserializar / analizar fácilmente desde javascript.
Jay Taylor
55
Debido a que obj puede no ser un objeto [hora, fecha, fecha y hora]
Rebs
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 supone que objestá en UTC si no reconoce la zona horaria. Usar en su datetime.utcnow()lugar.
jfs
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
40

Aquí está mi solución:

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

Entonces puedes usarlo así:

json.dumps(dictionnary, cls=DatetimeEncoder)
Natim
fuente
de acuerdo. Mucho mejor, al menos fuera del contexto 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
@Natim esta solución es la mejor. +1
Souvik Ray
20

Tengo una aplicación con un problema similar; mi enfoque era 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"}
codificación
fuente
No funciona si se ahorra el tiempo ahorrado haciendo datetime.utcnow ()
saurshaz
1
¿Qué error estás viendo con datetime.utcnow ()? Funciona bien para mi.
codingatty
17

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. Imprimirá:

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

Si quiere, más adelante puede 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)
fiatjaf
fuente
1
Debe usar isinstance (o, (datetime.date, datetime.datetime,)). Probablemente no estaría de más incluir datetime.time también.
Rebs
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
1
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. Serializar JSON debería funcionar. Al igual que la conversión de Unicode a ASCII debería. La incapacidad de Python para hacer esto sin funciones oscuras hace que sea molesto de usar. La validación de la base de datos es un problema separado de la OMI.
Rebs
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
2
JSON se usa comúnmente para cadenas, ints, flotantes, 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 / serialización. Si no fuera por esta pregunta, todavía estaría buscando manualmente en mis blobs json increíblemente complejos (para los cuales no siempre creé la estructura) para fechas y serializándolos 1 por 1.
Rebs
16

Este Q se repite una y otra vez, una forma simple de parchear el módulo json de modo que la serialización admita fecha y 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 siendo serializada como isoformat.

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

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

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

davidhadas
fuente
Parche de mono FTW. Por supuesto, lo desagradable es 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.
Jaap Versteegh
15

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)) 
Saurabh Saha
fuente
15

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

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), puede usar datetime.timestamp()y en su datetime.fromtimestamp()lugar;

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

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

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

Debe usar .strftime()método en .datetime.now()método para hacerlo como un método serializable .

Aquí hay 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'}
Benyamin Jafari
fuente
10

Aquí hay una solución simple para superar el problema "datetime no 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: -> {"fecha": "2015-12-16T04: 48: 20.024609"}

ob92
fuente
8

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

Sean Redmond
fuente
5

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

v_dict = version.dict()
v_dict['created_at'] = v_dict['created_at'].isoformat()
Peter Graham
fuente
5

En realidad es bastante simple. Si necesita serializar fechas con frecuencia, trabaje con ellas como cadenas. Puede convertirlos fácilmente de nuevo 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.

AngelDown
fuente
3

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 dados con una aplicación / json mimetype.

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

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, el archivo completo 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
reubano
fuente
1
La pregunta no tiene nada que ver con el matraz.
Zoran Pavlovic
2
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 cualquiera que lea esta pregunta y quiera una alternativa pymongo.
reubano
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 quite.
Zoran Pavlovic
3

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()
zhigang
fuente
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
macm
fuente
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

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, ints, flotantes y objetos de fecha y hora. No debería ser demasiado difícil de extender para otros tipos.

Hovo
fuente
1
Explota en Python 3 con TypeError: 'str' does not support the buffer interface. Es por 'wb'el modo abierto, debería ser 'w'. También sopla en la deserialización cuando tenemos datos similares a la fecha, '0000891618-05-000338'pero no coinciden con el patrón.
omikron
2

Convierte el date a string

date = str(datetime.datetime(somedatetimehere)) 
Rana Nematollahi
fuente
jjmontes respuesta hace exactamente eso, pero sin la necesidad de hacerlo de forma explícita para cada fecha ...
bluesummers
2

En general, 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 (los datos de la zona horaria se pierden), 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 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}}

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 flotante viene cuando se decodifica: si encuentra solo una cadena o especialmente int o flotante, necesita saber algo sobre los datos para saber si es una fecha y hora. Como dict, puede almacenar metadatos para que puedan decodificarse 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.

marca
fuente
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 (), para que ahora se vea así:

                  ...
                 'date_birth':self.date_birth.isoformat(),
                  ...
Treefish Zhang
fuente
1

Una solución rápida si quieres tu 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)
Arash
fuente
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 debe importar datetime 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.

ThunderBear
fuente
0

Encontré 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
naren
fuente
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
Vinod Kumar
fuente
0

Esta biblioteca superjson puede hacerlo. Y puede personalizar fácilmente el serializador json para su propio objeto Python siguiendo estas instrucciones 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 normal, una combinación de tipo genérico de python, dict, list, string, int, float. E implemente su método de diseño inversamente.

MacSanhe
fuente
-1

Puede que no sea 100% correcto, pero esta es la forma simple 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'> 
Shiva
fuente