2012-01-09 22 views
21

Ho una rappresentazione di stringa di un oggetto JSON.Come convertire in un oggetto datetime Python con JSON.loads?

dumped_dict = '{"debug": false, "created_at": "2020-08-09T11:24:20"}' 

Quando chiamo json.loads con questo oggetto;

json.loads(dumped_dict) 

Ottengo;

{'created_at': '2020-08-09T11:24:20', 'debug': False} 

Non c'è niente di male qui. Tuttavia, voglio sapere se c'è un modo per convertire l'oggetto di cui sopra con json.loads a qualcosa di simile:

{'created_at': datetime.datetime(2020, 08, 09, 11, 24, 20), 'debug': False} 

Poco, siamo in grado di convertire le stringhe datetime di oggetti datetime.datetime effettivi mentre chiamata json.loads?

risposta

17

mia soluzione finora:

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

Per ulteriore riferimento sull'uso di object_hook: JSON encoder and decoder

Nel mio caso la stringa JSON è venuta da una richiesta GET al mio API REST. Questa soluzione mi permette di 'ottenere la data giusta' in modo trasparente, senza forzare i clienti e gli utenti a prefissi hardcoding come __date__ in JSON, fino a quando la stringa di input è conforme al DATE_FORMAT che è:

DATE_FORMAT = '%a, %d %b %Y %H:%M:%S UTC' 

Il modello regex dovrebbe probabilmente essere ulteriormente raffinato

PS: nel caso ve lo stiate chiedendo, la stringa json_string è una query MongoDB/PyMongo.

+0

Si prega di fornire alcuni feedback/suggerimenti diversi da un semplice -1, quindi posso imparare qualcosa almeno :) –

+0

Absolutely saved me. – David

+0

@NicolaIarocci si presenta come una soluzione eccezionale, ma non obbliga i clienti a codificare un suffisso "UTC" nel loro json? –

1

Per quanto ne so non c'è dalla soluzione scatola per questo.

Prima di tutto, la soluzione deve tener conto json schema di distinguere correttamente tra stringhe e datetimes. In una certa misura si può intuire schema con lo schema JSON inferencer (google per lo schema JSON inferencer github) e poi fissare i luoghi che sono datetimes davvero.

Se lo schema è noto, dovrebbe essere abbastanza facile fare una funzione che analizza JSON e stringa sostituti rappresentazioni con datetime. Alcuni ispirazione per il codice potrebbe forse essere situato validictory prodotto (e la convalida dello schema JSON potrebbe essere anche una buona idea).

3

Il modo in cui viene inserita la domanda, non c'è alcuna indicazione per JSON che la stringa è un valore di data. Questo è diverso documentazione di json che ha la stringa esempio:

'{"__complex__": true, "real": 1, "imag": 2}' 

Questa stringa ha un indicatore "__complex__": true che può essere utilizzato per dedurre il tipo dei dati, ma a meno che un tale indicatore, una stringa è solo una stringa, e tutto ciò che puoi fare è regexp il tuo modo attraverso tutte le stringhe e decidere se sembrano date.

Nel tuo caso dovresti assolutamente usare uno schema se uno è disponibile per il tuo formato.

+0

Che tipo di documentazione di json propone di utilizzare nomi con doppio sottolineatura? Ho visto il tipo \ _ \ _, ad esempio, ma tutti sembrano convenzioni con uso limitato. –

+0

L'esempio è stato preso dalla documentazione del pacchetto 'json'. –

14

È necessario passare un oggetto object_hook.Dalla documentation:

object_hook è una funzione opzionale che verrà chiamata con il risultato di qualsiasi oggetto letterale decodificato (a dict). Verrà utilizzato il valore di ritorno di object_hook al posto del dict.

Ti piace questa:

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) 

Se anche voi volete gestire fusi orari dovrete utilizzare dateutil invece di strptime.

+1

L'utilizzo di try/catch come una struttura di controllo non è l'ideale. – Maciej

1

Si potrebbe utilizzare espressioni regolari per determinare se o non si desidera convertire un determinato campo per datetime in questo modo:

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 

Quindi è possibile fare riferimento alla funzione date_hook utilizzando il parametro object_hook nella chiamata 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) 
3

vorrei fare lo stesso come Nicola suggerito con 2 cambi:

  1. Utilizzare dateutil.parser anziché datetime.datetime.strptime
  2. Definire esplicitamente quali eccezioni voglio rilevare. Generalmente raccomandare di evitare ad ogni costo avente un vuoto except:

O in codice:

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

Direzione interessante da provare. Ma sembra un po 'lento per eseguire un analisi datetime su ogni elemento di JSON. La maggior parte degli articoli non saranno valori datetime. – swdev

0

Ispirato Nicola answer e atto a python3 (str anziché 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 

Questo evita di usare un meccanismo try/except. Il codice di prova PO:

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

La regex e datetime_format variabili possono essere facilmente adattati per misura altri modelli, ad esempio senza la T nel mezzo.

Per convertire una stringa salvata in isoformat (quindi memorizzata con microsecondi) in un oggetto datetime, fare riferimento a this question.

Problemi correlati