2016-06-16 5 views
8

Ich verwende SimpleJson, um JSON-Zeichenfolge zu Python-Objekten zu deserialisieren. Ich habe einen benutzerdefinierten geschriebenen object_hook, der sich um das Deserialisieren des JSON zurück zu meinen Domain-Objekten kümmert.Deserialisieren einer riesigen JSON-Zeichenfolge zu Python-Objekten

Das Problem ist, wenn meine JSON-String ist riesig (d. H. Der Server ist rund 800K Domain-Objekte in Form einer JSON-String zurück), dauert mein Python Deserializer fast 10 Minuten zu deserialisieren sie.

Ich habe ein bisschen weiter gedrillt und es sieht so aus, als würde simplejson als solches nicht viel Arbeit machen, sondern delegiert alles an den object_hook. Ich habe versucht, meinen object_hook zu optimieren, aber das verbessert auch nicht meine Leistung. (Ich habe kaum 1 Minute Verbesserung)

Meine Frage ist, haben wir irgendein anderes Standardframework, das für große Datenmengen optimiert ist oder gibt es einen Weg, wo ich die Fähigkeiten des Frameworks nutzen kann, anstatt alles auf object_hook Ebene zu tun .

Ich sehe, dass ohne object_hook das Framework nur eine Liste der Wörterbücher nicht Liste der Domänenobjekte zurückgibt.

Alle Zeiger hier sind nützlich.

FYI I simplejson Version 3.7.2

Hier bin mit ist meine Probe _object_hook:

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 

Eine Beispielantwort:

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

ich viele Verschachtelungsebenen haben und die Anzahl der Datensätze, die ich vom Server erhalte, sind mehr als 800K.

+0

Scheint interessant. Ein Beispiel-Snippet, um es schnell zu überprüfen, wäre nützlich. –

+0

Wenn Sie den Code Ihrer 'object_hook' Funktion und ein Beispiel des zu analysierenden JSONs posten könnten, würde uns das helfen, Ihre Frage zu beantworten. – jstlaurent

Antwort

6

Ich kenne kein Framework, das bietet, was Sie aus der Box suchen, aber Sie können ein paar Optimierungen auf die Art, wie Ihre Klasseninstanz eingerichtet ist, anwenden.

Da das Wörterbuch in Keyword-Argumente auspacken und sie auf Ihre Klassenvariablen anwendet, den Großteil der Zeit zu nehmen, können Sie die dct direkt auf Ihre Klasse vorbei betrachten __init__ und die Klasse Wörterbuch Einrichtung cls.__dict__ mit dct:

Versuch 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 

Versuch 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 

Es wird keine Sorgen über die self.__dict__ über eine weitere Referenz geändert werden, da der Verweis auf dct vor _object_hook kehrt verloren.

Dies bedeutet natürlich eine Änderung der Einrichtung Ihrer __init__, wobei die Attribute Ihrer Klasse streng von den Elementen in dct abhängen. Es liegt an dir.


Sie können auch cls != None mit cls is not None ersetzen (es gibt nur eine None Objekt so eine Identitätsprüfung mehr pythonic ist):

Versuch 1

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

Versuch 2

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

Und Sie können mit einem zwei Zeilen ersetzen:

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

werden:

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

Auf einer Skala von 800K Domain-Objekte, diese würden Sie einige sparen Gute Zeit auf die object_hook, um Ihre Objekte schneller zu erstellen.

+1

Danke für den Blick hinein. Mit Ihren Vorschlägen kann ich die Deserialisierungszeit von object_hook um 2 Minuten reduzieren. Aber die Endzeit für 800K ist ~ 8 Minuten. Ich sehe für 800K Datensätze mein object_hook wird von simplejson "3709170" oft aufgerufen. Ich habe mich gefragt, ob es ein Framework gibt, das optimiert ist, um diese Anrufe zu reduzieren. Irgendwelche Gedanken über lambdaJSON (jsontree/jsonpickle oder irgendeinen anderen Rahmen) – pragnya

+0

@pragnya Wenn es gut mit 'lamdaJSON' klappt, kannst du deinen Hack als eine Antwort für andere posten, die das gleiche Problem in der Zukunft haben könnten. –