Сериализация объекта suds в python - PullRequest
15 голосов
/ 09 марта 2010

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

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

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

Обновление Чтение верхнего ответа на Как я могу получить результаты? дает мне лучшее представление о том, почему это не вариант, я думаю, что я застрял, воссоздавая базовый объект с необходимой мне информацией?

Ответы [ 6 ]

32 голосов
/ 28 марта 2013

Я использовал следующий подход для преобразования объекта 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))
6 голосов
/ 10 марта 2010

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

5 голосов
/ 30 января 2013

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

Теперь, после вызова suds, например, ниже:

# 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)

Надеюсь, это поможет.

2 голосов
/ 19 сентября 2017

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

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

from suds.sudsobject import asdict, Factory as SudsFactory

def suds2dict(d):                                                               
    """                                                                         
    Suds object serializer 
    Borrowed from /1521026/serializatsiya-obekta-suds-v-python#1521036                                                     
    """                                                                         
    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)  
2 голосов
/ 18 января 2016

Вот то, что я придумал, прежде чем исследовать и найти этот ответ.На самом деле это хорошо работает для сложных ответов suds, а также для других объектов, таких как __builtins__, поскольку решение не зависит от suds:

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

1 голос
/ 13 января 2016

Я обновил recursive_asdict пример выше , чтобы он был совместим с 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
...