【问题标题】:Deserializing a huge json string to python objects将一个巨大的 json 字符串反序列化为 python 对象
【发布时间】:2016-10-18 02:51:08
【问题描述】:

我正在使用 simplejson 将 json 字符串反序列化为 python 对象。我有一个自定义编写的 object_hook,负责将 json 反序列化回我的域对象。

问题是,当我的 json 字符串很大时(即服务器以 json 字符串的形式返回大约 800K 域对象),我的 python 反序列化器需要将近 10 分钟来反序列化它们。

我进一步深入研究,看起来 simplejson 并没有做太多工作,而是将所有内容委托给 object_hook。我尝试优化我的 object_hook 但这也没有提高我的性能。 (我几乎没有 1 分钟的改进)

我的问题是,我们是否有任何其他标准框架经过优化以处理庞大的数据集,或者有没有一种方法可以让我利用框架的功能,而不是在 object_hook 级别做所有事情。

我看到,如果没有 object_hook,框架只返回一个字典列表,而不是域对象列表。

这里的任何指针都会很有用。

仅供参考,我使用的是 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。

【问题讨论】:

  • 看起来很有趣。任何用于快速检查的示例 sn-p 都会很有用。
  • 如果您可以发布您的 object_hook 函数的代码和您要解析的 JSON 示例,这将有助于我们回答您的问题。

标签: python json performance python-2.7 simplejson


【解决方案1】:

我不知道有什么框架可以提供开箱即用的功能,但您可以对类实例的设置方式进行一些优化。

由于将字典解压缩为关键字参数并将它们应用于您的类变量需要花费大量时间,您可以考虑将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__ 被另一个引用修改,因为在 _object_hook 返回之前对 dct 的引用丢失了。

这当然意味着更改您的__init__ 的设置,您的班级属性严格取决于dct 中的项目。这取决于你。


您也可以将cls != None 替换为cls is not None(只有一个None 对象,因此身份检查更符合pythonic):

试用 1

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

试用 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 以更快地创建对象的好时间。

【讨论】:

  • 感谢您的关注。根据您的建议,我可以减少 2 分钟的 object_hook 反序列化时间。但是 800K 的结束时间仍然是 ~8 分钟。我看到 800K 记录我的 object_hook 被 simplejson "3709170" 次数调用。我想知道是否有任何框架经过优化以减少此调用。关于 lambdaJSON(jsontree/jsonpickle 或任何其他框架)的任何想法
  • @pragnya 如果它与lamdaJSON 配合得很好,您可以将您的技巧发布为将来可能遇到相同问题的其他人的答案。
猜你喜欢
  • 1970-01-01
  • 2013-03-06
  • 1970-01-01
  • 2016-06-20
  • 2019-02-09
  • 2014-01-04
  • 2020-02-03
  • 1970-01-01
  • 2012-04-24
相关资源
最近更新 更多