2016-06-16 33 views
8

Sto usando simplejson per deserializzare la stringa json in oggetti python. Ho un object_hook personalizzato che si occupa della deserializzazione del json nei miei oggetti di dominio.Deserializzazione di un'enorme stringa json su oggetti python

Il problema è che quando la mia stringa json è enorme (ovvero il server restituisce oggetti di dominio 800 KB sotto forma di una stringa json), il mio deserializzatore Python impiega quasi 10 minuti per deserializzarli.

Mi sono perforato un po 'più in là e sembra un semplice json in quanto tale non sta facendo molto lavoro, piuttosto sta delegando tutto a object_hook. Ho provato a ottimizzare il mio object_hook ma anche questo non migliora le mie prestazioni. (Difficilmente ho ottenuto 1 miglioramento minimo)

La mia domanda è, abbiamo qualche altro framework standard che è ottimizzato per gestire enormi set di dati o c'è un modo in cui posso utilizzare le capacità del framework piuttosto che fare tutto a livello object_hook .

Vedo che senza object_hook il framework restituisce solo un elenco di dizionari non un elenco di oggetti di dominio.

Qualsiasi suggerimento qui sarà utile.

FYI Sto usando simplejson versione 3.7.2

Ecco il mio _object_hook campione:

def _object_hook(dct): 
    if '@CLASS' in dct: # server sends domain objects with this @CLASS 
     clsname = dct['@CLASS'] 
     # This is like Class.forName (This imports the module and gives the class) 
     cls = get_class(clsname) 
     # As my server is in java, I convert the attributes to python as per python naming convention. 
     dct = dict((convert_java_name_to_python(k), dct[k]) for k in dct.keys()) 
     if cls != None: 
      obj_key = None 
      if "@uuid"in dct 
       obj_key = dct["@uuid"] 
       del(dct["@uuid"]) 
      else: 
       info("Class missing uuid: " + clsname) 
      dct.pop("@CLASS", None) 

      obj = cls(**dct) #This I found to be the most time consuming process. In my domian object, in the __init__ method I have the logic to set all attributes based on the kwargs passed 
      if obj_key is not None: 
       shared_objs[obj_key] = obj #I keep all uuids along with the objects in shared_objs dictionary. This shared_objs will be used later to replace references. 
     else: 
      warning("class not found: " + clsname) 
      obj = dct 

     return obj 
    else: 
     return dct 

Una risposta Esempio:

{"@CLASS":"sample.counter","@UUID":"86f26a0a-1a58-4429-a762- 9b1778a99c82","val1":"ABC","val2":1131,"val3":1754095,"value4": {"@CLASS":"sample.nestedClass","@UUID":"f7bb298c-fd0b-4d87-bed8- 74d5eb1d6517","id":1754095,"name":"XYZ","abbreviation":"ABC"}} 

Ho molti livelli di nidificazione e il numero di record che sto ricevendo dal server è più di 800K.

+0

Sembra interessante. Sarebbe utile qualsiasi frammento di esempio per controllarlo rapidamente. –

+0

Se potessi pubblicare il codice della funzione 'object_hook' e un campione del JSON che vuoi analizzare, questo ci aiuterebbe a rispondere alla tua domanda. – jstlaurent

risposta

6

Non conosco alcun framework che offra ciò che si cerca, ma è possibile applicare alcune ottimizzazioni al modo in cui è stata configurata l'istanza della classe.

Dal momento disimballare il dizionario in argomenti a parola chiave e la loro applicazione per le variabili di classe sta prendendo la maggior parte del tempo, si può considerare superato il dct direttamente alla classe __init__ e impostazione del dizionario di classe cls.__dict__ con dct:

Trial 1

In [1]: data = {"name": "yolanda", "age": 4} 

In [2]: class Person: 
    ...:  def __init__(self, name, age): 
    ...:   self.name = name 
    ...:   self.age = age 
    ...: 
In [3]: %%timeit 
    ...: Person(**data) 
    ...: 
1000000 loops, best of 3: 926 ns per loop 

Trial 2

In [4]: data = {"name": "yolanda", "age": 4} 

In [5]: class Person2: 
    ....:  def __init__(self, data): 
    ....:   self.__dict__ = data 
    ....: 
In [6]: %%timeit 
    ....: Person2(data) 
    ....: 
1000000 loops, best of 3: 541 ns per loop 

Non ci saranno preoccupazioni circa la self.__dict__ essere modificati tramite un altro riferimento in quanto il riferimento al dct si perde prima di _object_hook ritorni.

Ciò ovviamente significherebbe modificare l'impostazione del proprio __init__, con gli attributi della classe strettamente dipendenti dagli articoli in dct. Tocca a voi.


Si può anche sostituire cls != None con cls is not None (c'è solo un None oggetto in modo un controllo d'identità è più divinatorio):

Trial 1

In [38]: cls = 5 
In [39]: %%timeit 
    ....: cls != None 
    ....: 
10000000 loops, best of 3: 85.8 ns per loop 

Trial 2

In [40]: %%timeit 
    ....: cls is not None 
    ....: 
10000000 loops, best of 3: 57.8 ns per loop 

E si può sostituire due linee con una con:

obj_key = dct["@uuid"] 
del(dct["@uuid"]) 

diventare:

obj_key = dct.pop('@uuid') # Not an optimization as this is same with the above 

Su una scala da 800K dominio oggetti, questi si sarebbe risparmiare un po ' buon momento per ottenere il object_hook per creare i tuoi oggetti più rapidamente.

+1

Grazie per averci seguito. Con i tuoi suggerimenti, sono in grado di ridurre i 2 minuti del tempo di deserializzazione object_hook. Ma il tempo di end-end per 800K è ancora ~ 8 minuti. Vedo per i record 800K il mio object_hook è chiamato da simplejson "3709170" numero di volte. Mi stavo chiedendo se c'è qualche framework che è ottimizzato per ridurre queste chiamate. Qualche idea su lambdaJSON (jsontree/jsonpickle o qualsiasi altro framework) – pragnya

+0

@pragnya Se funziona bene con 'lamdaJSON', puoi pubblicare il tuo hack come risposta per gli altri che potrebbero avere lo stesso problema in futuro. –