2016-06-16 10 views
8

Я использую simplejson для десериализации строки json для объектов python. У меня есть собственный написанный объект object_hook, который заботится о десериализации json обратно на мои объекты домена.Deserializing огромная строка json для объектов python

Проблема заключается в том, что когда моя строка json огромна (т. Е. Сервер возвращает около 800K объектов домена в виде строки json), мой десериализатор python занимает почти 10 минут, чтобы десериализовать их.

Я просверлился немного дальше, и похоже, что simplejson как таковой не делает много работы, а делегирует все объекту object_hook. Я попытался оптимизировать свой объект object_hook, но это тоже не улучшает мою производительность. (Я едва получил улучшение в 1 мин.)

Мой вопрос в том, есть ли у нас какая-либо другая стандартная структура, оптимизированная для обработки огромного набора данных, или есть способ, в котором я могу использовать возможности фреймворка, а не делать все на уровне object_hook ,

Я вижу, что без object_hook Framework возвращает только список словарей, а не список объектов домена.

Любые указатели здесь будут полезны.

FYI Я использую simplejson версии 3.7.2

Вот мой пример _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 

Ответ Пример:

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

У меня есть много уровней вложенности и числа записей, которые я получаю с сервера, составляет более 800K.

+0

Кажется интересным. Любой образец фрагмента, чтобы быстро проверить его, было бы полезно. –

+0

Если бы вы могли опубликовать код своей функции 'object_hook' и образец JSON, который вы хотите проанализировать, это поможет нам ответить на ваш вопрос. – jstlaurent

ответ

6

Я не знаю каких-либо фреймворков, которые предлагают то, что вы ищете из коробки, но вы можете применить несколько оптимизаций к тому, как настроен экземпляр вашего класса.

Поскольку распаковка словаря в аргументы ключевых слов и применяя их к переменному классу занимают большую часть времени, вы можете рассмотреть возможность прохождения dct непосредственно к классу __init__ и настройке словаря класса cls.__dict__ с dct:

Испытание 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 

Испытание 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 

Там не будет никаких забот о self.__dict__ модифицируется через другую ссылку, так как ссылка на dct теряется, прежде чем _object_hook возвращается.

Это, конечно же, означает изменение настройки вашего __init__, с атрибутами вашего класса строго в зависимости от предметов в dct. Тебе решать.


Вы также можете заменить cls != None с cls is not None (есть только один None объект так что проверка идентичности более вещий):

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 

И вы можете заменить две строки с одной с:

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

становится:

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

По шкале 800K области объектов, это позволит сэкономить вам некоторые хорошее время на получение object_hook, чтобы быстрее создавать объекты.

+1

Спасибо, что посмотрели на него. С вашими предложениями я могу уменьшить 2 минуты времени десериализации объекта_хука. Но все же конечное время для 800K составляет ~ 8 минут. Я вижу, что для записей 800K мой object_hook вызывается простым словом «3709170». Мне было интересно, есть ли какая-либо инфраструктура, оптимизированная для сокращения этих вызовов. Любые мысли о lambdaJSON (jsontree/jsonpickle или любые другие рамки) – pragnya

+0

@pragnya Если это хорошо работает с 'lamdaJSON', вы можете опубликовать свой взлом в качестве ответа для других, у кого может быть такая же проблема в будущем. –

 Смежные вопросы

  • Нет связанных вопросов^_^