【问题标题】:Serializing a suds object in python在python中序列化一个suds对象
【发布时间】:2010-03-09 20:54:08
【问题描述】:

好的,我正在努力使 python 变得更好,所以我不确定这是开始我正在做的事情的正确方法,但这是我目前的问题......

我需要通过 SOAP 方法获取一些信息,现在只使用部分信息,但存储整个结果以备将来使用(我们需要尽可能少地使用服务)。寻找访问服务的最佳方式我认为 suds 是要走的路,而且它很简单,并且像获取数据的魅力一样工作。但是现在我想以某种方式保存结果,最好是序列化 / 在数据库中,以便以后可以将其拉出并使用它。

最好的方法是什么,看起来 pickle/json 不是一个选项?谢谢!

更新 阅读How can I pickle suds results? 的最佳答案让我更好地了解为什么这不是一个选项,我想我无法使用我需要的信息重新创建一个基本对象?

【问题讨论】:

    标签: python soap pickle suds


    【解决方案1】:

    我一直在使用以下方法将 Suds 对象转换为 JSON:

    from suds.sudsobject import asdict
    
    def recursive_asdict(d):
        """Convert Suds object into serializable format."""
        out = {}
        for k, v in asdict(d).items():
            if hasattr(v, '__keylist__'):
                out[k] = recursive_asdict(v)
            elif isinstance(v, list):
                out[k] = []
                for item in v:
                    if hasattr(item, '__keylist__'):
                        out[k].append(recursive_asdict(item))
                    else:
                        out[k].append(item)
            else:
                out[k] = v
        return out
    
    def suds_to_json(data):
        return json.dumps(recursive_asdict(data))
    

    【讨论】:

    • 请注意,该函数不适用于 suds 对象列表。这可以通过[recursive_asdict(d) for d in ds] 解决。
    • @Jon 由于elif isinstance(v, list) 检查,它确实有效。
    • 使用这个解决方案我得到了错误:AttributeError: 'dict' object has no attribute 'iteritems'
    • @GuilhermeMatheus 呵呵,此代码适用于 Python2。我希望它现在已修复:)
    • @Rafay 我能够使用参数retxml=True 将 XML 响应中的数据解析为 JSON 对象。但是非常感谢您快速编辑的回复!
    【解决方案2】:

    是的,我确认我在您所指的答案中给出的解释-动态生成的类不容易腌制(也不容易序列化),您需要提取所有状态信息,腌制 状态,如果你真的坚持使用它,并在检索时重建棘手的 sudsobject;-)。

    【讨论】:

    • 我正在使用 'retxml=True' 选项来获取原始结果,然后现在将其转换为 dict(应该是可序列化的) - 看起来它可以工作......谢谢!
    • @pssdbt,太好了——或者您可以序列化 XML 本身,但我相信解开腌制的 dict 会比解析 XML 更快,所以我认为您做出了正确的选择。
    【解决方案3】:

    这是我在研究和找到这个答案之前想出的。这实际上适用于复杂的泡沫响应以及其他对象(例如__builtins__),因为该解决方案与泡沫无关:

    import datetime
    
    def object_to_dict(obj):
        if isinstance(obj, (str, unicode, bool, int, long, float, datetime.datetime, datetime.date, datetime.time)):
            return obj
        data_dict = {}
        try:
            all_keys = obj.__dict__.keys()  # vars(obj).keys()
        except AttributeError:
            return obj
        fields = [k for k in all_keys if not k.startswith('_')]
        for field in fields:
            val = getattr(obj, field)
            if isinstance(val, (list, tuple)):
                data_dict[field] = []
                for item in val:
                    data_dict[field].append(object_to_dict(item))
            else:
                data_dict[field] = object_to_dict(val)
        return data_dict
    

    此解决方案有效,实际上速度更快。它也适用于没有__keylist__ 属性的对象。

    我在一个复杂的泡沫输出对象上运行了 100 次基准测试,该解决方案的运行时间为 0.04 到 0.052 秒(平均为 0.045724287)。虽然上述recursive_asdict 解决方案的运行时间为 0.082 到 0.102 秒,但几乎翻了一番(平均为 0.0829765582)。

    然后我回到绘图板并重新执行该函数以从中获得更多性能,它不需要datetime 导入。我利用了__keylist__ 属性,因此这不适用于其他对象,例如__builtins__,但对于 suds 对象输出效果很好:

    def fastest_object_to_dict(obj):
        if not hasattr(obj, '__keylist__'):
            return obj
        data = {}
        fields = obj.__keylist__
        for field in fields:
            val = getattr(obj, field)
            if isinstance(val, list):  # tuple not used
                data[field] = []
                for item in val:
                    data[field].append(fastest_object_to_dict(item))
            else:
                data[field] = fastest_object_to_dict(val)
        return data
    

    运行时间为 0.18 - 0.033 秒(平均为 0.0260889721),比recursive_asdict 解决方案快近 4 倍。

    【讨论】:

      【解决方案4】:

      我为 suds 的 Object 实例实现了一个虚拟类,然后能够序列化。 FakeSudsInstance 的行为类似于原始的 Suds 对象实例,如下所示:

      from suds.sudsobject import Object as SudsObject
      
      class FakeSudsNode(SudsObject):
      
          def __init__(self, data):
              SudsObject.__init__(self)
              self.__keylist__ = data.keys()
              for key, value in data.items():
                  if isinstance(value, dict):
                      setattr(self, key, FakeSudsNode(value))
                  elif isinstance(value, list):
                      l = []
                      for v in value:
                          if isinstance(v, list) or isinstance(v, dict):
                              l.append(FakeSudsNode(v))
                          else:
                              l.append(v)
                      setattr(self, key, l)
                  else:
                      setattr(self, key, value)
      
      
      class FakeSudsInstance(SudsObject):
      
          def __init__(self, data):
              SudsObject.__init__(self)
              self.__keylist__ = data.keys()
              for key, value in data.items():
                  if isinstance(value, dict):
                      setattr(self, key, FakeSudsNode(value))
                  else:
                      setattr(self, key, value)
      
          @classmethod
          def build_instance(cls, instance):
              suds_data = {}
              def node_to_dict(node, node_data):
                  if hasattr(node, '__keylist__'):
                      keys = node.__keylist__
                      for key in keys:
                          if isinstance(node[key], list):
                              lkey = key.replace('[]', '')
                              node_data[lkey] = node_to_dict(node[key], [])
                          elif hasattr(node[key], '__keylist__'):
                              node_data[key] = node_to_dict(node[key], {})
                          else:
                              if isinstance(node_data, list):
                                  node_data.append(node[key])
                              else:
                                  node_data[key] = node[key]
                      return node_data
                  else:
                      if isinstance(node, list):
                          for lnode in node:
                              node_data.append(node_to_dict(lnode, {}))
                          return node_data
                      else:
                          return node
              node_to_dict(instance, suds_data)
              return cls(suds_data)
      

      现在,在一次泡沫调用之后,例如下面:

      # Now, after a suds call, for example below
      >>> import cPickle as pickle
      >>> suds_intance = client.service.SomeCall(account, param)
      >>> fake_suds = FakeSudsInstance.build_instance(suds_intance)
      >>> dumped = pickle.dumps(fake_suds)
      >>> loaded = pickle.loads(dumped)
      

      希望对你有帮助。

      【讨论】:

      • node_data.append(node_to_dict(lnode, {})) 是错误的,因为它将字典传递给 node_to_dict。
      • return node_data inside if hasattr(node, '__keylist__'): 也应该返回 FakeSudsNode(node_data) 因为它组成一个字典
      【解决方案5】:

      上面建议的解决方案丢失了关于类名的有价值信息 - 它在某些库中可能很有价值,例如 DFP 客户端 https://github.com/googleads/googleads-python-lib,其中实体类型可能被编码在动态生成的类名中(即 TemplateCreative/ ImageCreative)

      这是我使用的解决方案,它保留类名并恢复 dict 序列化对象而不会丢失数据(suds.sax.text.Text 除外,它将被转换为常规的 unicode 对象,可能还有其他一些类型我没遇到过)

      from suds.sudsobject import asdict, Factory as SudsFactory
      
      def suds2dict(d):                                                               
          """                                                                         
          Suds object serializer 
          Borrowed from https://stackoverflow.com/questions/2412486/serializing-a-suds-object-in-python/15678861#15678861                                                     
          """                                                                         
          out = {'__class__': d.__class__.__name__}                                   
          for k, v in asdict(d).iteritems():                                          
              if hasattr(v, '__keylist__'):                                           
                  out[k] = suds2dict(v)                                               
              elif isinstance(v, list):                                               
                  out[k] = []                                                         
                  for item in v:                                                      
                      if hasattr(item, '__keylist__'):                                
                          out[k].append(suds2dict(item))                              
                      else:                                                           
                          out[k].append(item)                                         
              else:                                                                   
                  out[k] = v                                                          
          return out                                                                  
      
      
      def dict2suds(d):                                                               
          """                                                                         
          Suds object deserializer                                                    
          """                                                                         
          out = {}                                                                    
          for k, v in d.iteritems():                                                  
              if isinstance(v, dict):                                                 
                  out[k] = dict2suds(v)                                               
              elif isinstance(v, list):                                               
                  out[k] = []                                                         
                  for item in v:                                                      
                      if isinstance(item, dict):                                      
                          out[k].append(dict2suds(item))                              
                      else:                                                           
                          out[k].append(item)                                         
              else:                                                                   
                  out[k] = v                                                          
          return SudsFactory.object(out.pop('__class__'), out)  
      

      【讨论】:

      • 只是指出,对于 Python 3,将 iteritems() 替换为 items()。感谢您的精彩回答!
      • 我只需要 dict2suds 部分并且在 out.pop('__class__') 部分中有错误,但将其删除并直接替换为 .object(dict=out) 为我的情况修复了它。谢谢你的提示!
      【解决方案6】:

      我更新了recursive_asdict 示例above 以与python3 兼容(items 而不是iteritems)。

      from suds.sudsobject import asdict
      from suds.sax.text import Text
      
      def recursive_asdict(d):
          """
          Recursively convert Suds object into dict.
          We convert the keys to lowercase, and convert sax.Text
          instances to Unicode.
      
          Taken from:
          https://stackoverflow.com/a/15678861/202168
      
          Let's create a suds object from scratch with some lists and stuff
          >>> from suds.sudsobject import Object as SudsObject
          >>> sudsobject = SudsObject()
          >>> sudsobject.Title = "My title"
          >>> sudsobject.JustAList = [1, 2, 3]
          >>> sudsobject.Child = SudsObject()
          >>> sudsobject.Child.Title = "Child title"
          >>> sudsobject.Child.AnotherList = ["4", "5", "6"]
          >>> childobject = SudsObject()
          >>> childobject.Title = "Another child title"
          >>> sudsobject.Child.SudObjectList = [childobject]
      
          Now see if this works:
          >>> result = recursive_asdict(sudsobject)
          >>> result['title']
          'My title'
          >>> result['child']['anotherlist']
          ['4', '5', '6']
         """
          out = {}
          for k, v in asdict(d).items():
              k = k.lower()
              if hasattr(v, '__keylist__'):
                  out[k] = recursive_asdict(v)
              elif isinstance(v, list):
                  out[k] = []
                  for item in v:
                      if hasattr(item, '__keylist__'):
                          out[k].append(recursive_asdict(item))
                      else:
                          out[k].append(
                              item.title() if isinstance(item, Text) else item)
              else:
                  out[k] = v.title() if isinstance(v, Text) else v
          return out
      

      【讨论】:

        【解决方案7】:

        我喜欢这种方式。我们自己不做迭代,转成字符串的时候是python迭代的

        class Ob:
            def __init__(self, J) -> None:
                self.J = J
            
            def __str__(self):           
        
                if hasattr(self.J, "__keylist__"):
                    self.J = {key: Ob(value) for key, value in dict(self.J).items()}
                if hasattr(self.J, "append"):
                    self.J = [Ob(data) for data in sefl.J]
                return str(self.J)
        
        result = Ob(result_soap)
        

        【讨论】:

          猜你喜欢
          • 2015-07-27
          • 2010-12-29
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-10-19
          相关资源
          最近更新 更多