2010-03-09 2 views
14

Хорошо, я работаю над улучшением работы с python, поэтому я не уверен, что это правильный способ, чтобы начать то, что я делаю для начала, но вот моя текущая проблема. .Сериализация объекта suds в python

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

Каков наилучший способ сделать это, похоже, что pickle/json - это не вариант? Благодаря!

Update Чтения верхнего ответ на How can I pickle suds results? дает мне лучшее представление о том, почему это не вариант, я предполагаю, что я застрял воссоздание основного объекта ж/информации мне нужно?

ответ

6

Да, я подтверждаю объяснение я дал в ответ вы ссылаетесь - динамически создаваемые классы не легко пригодны для консервирования (ни в противном случае легко сериализации), вам нужно извлечь всю информацию о состоянии, рассол что состояние, и реконструируйте сложный sudsobject при поиске, если вы действительно настаиваете на его использовании ;-).

+5

Я использую параметр «retxml = True», чтобы вернуть исходный результат обратно, а затем превратить это в dict (который должен быть сериализуемым) сейчас - похоже, что он будет работать ... спасибо! – jeffff

+2

@pssdbt, отлично - или вы могли бы сериализовать сам XML, но я считаю, что распиловка маринованного диктатора будет быстрее, чем анализ XML, поэтому я думаю, что вы сделали правильный выбор. –

5

Я сделал реализацию фиктивного класса для Object intance suds, а затем смог сериализовать. 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) 

Я надеюсь, что это помогает.

+0

'node_data.append (node_to_dict (lnode, {}))' неверно, поскольку он передает dict в node_to_dict. –

+0

'return node_data' внутри' if hasattr (node, '__keylist __'): 'должен также возвращать FakeSudsNode (node_data) с момента его создания dict –

31

Я использовал следующий подход для преобразования Suds объекта в JSON:

from suds.sudsobject import asdict 

def recursive_asdict(d): 
    """Convert Suds object into serializable format.""" 
    out = {} 
    for k, v in asdict(d).iteritems(): 
     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)) 
+0

Обратите внимание, что функция не работает со списком объектов suds. Это можно решить с помощью '[recursive_asdict (d) для d в ds]'. – Jon

+2

@Jon Это работает из-за 'elif isinstance (v, list)' check. – Rafay

1

Я обновил 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 
2

Вот что я придумал, прежде чем исследовать и находить этот ответ. Это на самом деле работает хорошо для меня на сложных пенообразования ответов, а также на других объектах, таких как __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,82 до 0,102 секунды, что почти удвоилось (0,0829765582 в среднем).

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

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 средняя), так что почти 4x как быстрее, чем решение recursive_asdict.

1

Решения suggesed выше потерять ценную информацию об именах классов - это может быть полезным в некоторых библиотеках, как DFP клиент https://github.com/googleads/googleads-python-lib где типы сущностей могут быть закодированы в динамически создаваемых именах классов (т.е. TemplateCreative/ImageCreative)

Вот решение, которое я использовал, которое сохраняет имена классов и восстанавливает объекты с сериализацией dict без потери данных (кроме suds.sax.text.Text, которые будут преобразованы в обычные объекты юникода и, возможно, некоторые другие типы, с которыми я не сталкивался)

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) 
Смежные вопросы