not - python parse date
¿Cómo convertir a un objeto datetime de Python con JSON.loads? (7)
De la forma en que se coloca su pregunta, no hay ninguna indicación de que la cadena sea un valor de fecha. Esto es diferente a la documentación de json que tiene la cadena de ejemplo:
''{"__complex__": true, "real": 1, "imag": 2}''
Esta cadena tiene un indicador "__complex__": true
que se puede usar para inferir el tipo de datos, pero a menos que exista tal indicador, una cadena es solo una cadena, y todo lo que puede hacer es "__complex__": true
una "__complex__": true
través de todas las cadenas y decidir si se parecen a las fechas.
En su caso, definitivamente debería usar un esquema si hay uno disponible para su formato.
Tengo una representación de cadena de un objeto JSON.
dumped_dict = ''{"debug": false, "created_at": "2020-08-09T11:24:20"}''
Cuando llamo a json.loads con este objeto;
json.loads(dumped_dict)
Yo obtengo;
{''created_at'': ''2020-08-09T11:24:20'', ''debug'': False}
No hay nada malo aquí. Sin embargo, quiero saber si hay una manera de convertir el objeto anterior con json.loads a algo como esto:
{''created_at'': datetime.datetime(2020, 08, 09, 11, 24, 20), ''debug'': False}
En breve, ¿podemos convertir cadenas datetime en objetos datetime.datetime reales al llamar a json.loads?
Haría lo mismo que Nicola sugirió con 2 cambios:
- Use
dateutil.parser
lugar dedatetime.datetime.strptime
- Definir explícitamente qué excepciones quiero capturar. Generalmente recomiendo evitar a toda costa tener un vacío
except:
O en el código:
import dateutil.parser
def datetime_parser(json_dict):
for (key, value) in json_dict.items():
try:
json_dict[key] = dateutil.parser.parse(value)
except (ValueError, AttributeError):
pass
return json_dict
str = "{...}" # Some JSON with date
obj = json.loads(str, object_hook=datetime_parser)
print(obj)
Inspirado por la answer de Nicola y adaptado a python3 (str en lugar de basestring):
import re
from datetime import datetime
datetime_format = "%Y-%m-%dT%H:%M:%S"
datetime_format_regex = re.compile(r''^/d{4}-/d{2}-/d{2}T/d{2}:/d{2}:/d{2}$'')
def datetime_parser(dct):
for k, v in dct.items():
if isinstance(v, str) and datetime_format_regex.match(v):
dct[k] = datetime.strptime(v, datetime_format)
return dct
Esto evita el uso de un mecanismo try / except. En el código de prueba de OP:
>>> import json
>>> json_string = ''{"debug": false, "created_at": "2020-08-09T11:24:20"}''
>>> json.loads(json_string, object_hook=datetime_parser)
{''created_at'': datetime.datetime(2020, 8, 9, 11, 24, 20), ''debug'': False}
Las variables regex y datetime_format
pueden adaptarse fácilmente para adaptarse a otros patrones, por ejemplo, sin la T en el medio.
Para convertir una cadena guardada en isoformato (por lo tanto, almacenada con microsegundos) en un objeto de fecha y hora, consulte esta pregunta .
Mi solución hasta ahora:
>>> json_string = ''{"last_updated": {"$gte": "Thu, 1 Mar 2012 10:00:49 UTC"}}''
>>> dct = json.loads(json_string, object_hook=datetime_parser)
>>> dct
{u''last_updated'': {u''$gte'': datetime.datetime(2012, 3, 1, 10, 0, 49)}}
def datetime_parser(dct):
for k, v in dct.items():
if isinstance(v, basestring) and re.search("/ UTC", v):
try:
dct[k] = datetime.datetime.strptime(v, DATE_FORMAT)
except:
pass
return dct
Para mayor referencia sobre el uso de object_hook: codificador y decodificador JSON
En mi caso, la cadena json proviene de una solicitud GET a mi API REST. Esta solución me permite ''obtener la fecha correcta'' de manera transparente, sin forzar a los clientes y usuarios a usar prefijos de __date__
como __date__
en el JSON, siempre que la cadena de entrada se ajuste a DATE_FORMAT, que es:
DATE_FORMAT = ''%a, %d %b %Y %H:%M:%S UTC''
El patrón regex probablemente debería ser refinado aún más
PD: en caso de que se lo pregunte, json_string es una consulta de MongoDB / PyMongo.
Necesitas pasar un object_hook . De la documentation :
object_hook es una función opcional que se llamará con el resultado de cualquier objeto decodificado literalmente (un dict). El valor de retorno de object_hook se utilizará en lugar del dict.
Me gusta esto:
import datetime
import json
def date_hook(json_dict):
for (key, value) in json_dict.items():
try:
json_dict[key] = datetime.datetime.strptime(value, "%Y-%m-%dT%H:%M:%S")
except:
pass
return json_dict
dumped_dict = ''{"debug": false, "created_at": "2020-08-09T11:24:20"}''
loaded_dict = json.loads(dumped_dict, object_hook=date_hook)
Si también quiere manejar zonas horarias, tendrá que usar dateutil en lugar de strptime.
Por lo que sé, no hay una solución inmediata para esto.
En primer lugar, la solución debe tener en cuenta el esquema json para distinguir correctamente entre cadenas y tiempos de datos. Hasta cierto punto, puede adivinar el esquema con el inferencer de esquema de json (google for json schema inferencer github) y luego corregir los lugares que son realmente fechas de datos.
Si se conoce el esquema, debería ser bastante fácil hacer una función, que analiza json y sustituye las representaciones de cadena con datetime. Tal vez se pueda encontrar alguna inspiración para el código en el producto de validictory (y la validación del esquema json también podría ser una buena idea).
Puede usar expresiones regulares para determinar si desea o no convertir un campo determinado a fecha y hora como:
def date_hook(json_dict):
for (key, value) in json_dict.items():
if type(value) is str and re.match(''^/d{4}-/d{2}-/d{2}T/d{2}:/d{2}:/d{2}/./d*$'', value):
json_dict[key] = datetime.datetime.strptime(value, "%Y-%m-%dT%H:%M:%S.%f")
elif type(value) is str and re.match(''^/d{4}-/d{2}-/d{2}T/d{2}:/d{2}:/d{2}$'', value):
json_dict[key] = datetime.datetime.strptime(value, "%Y-%m-%dT%H:%M:%S")
else:
pass
return json_dict
Luego puede hacer referencia a la función date_hook usando el parámetro object_hook en su llamada a json.loads ():
json_data = ''{"token": "faUIO/389KLDLA", "created_at": "2016-09-15T09:54:20.564"}''
data_dictionary = json.loads(json_data, object_hook=date_hook)