2010-03-09 23 views
14

Ok, estoy trabajando para mejorar con Python, así que no estoy seguro de que esta sea la manera correcta de hacer lo que estoy haciendo para empezar, pero este es mi problema actual.Serializar un objeto de espuma en python

Necesito obtener información a través de un método SOAP, y ahora solo uso parte de la información pero almaceno el resultado completo para usos futuros (necesitamos usar el servicio lo menos posible). Buscando la mejor manera de acceder al servicio, pensé que la espuma era el camino a seguir, y fue simple y funcionó como un amuleto para obtener los datos. Pero ahora quiero guardar el resultado de alguna manera, preferiblemente serializado/en una base de datos para poder sacarlo más tarde y usarlo de la misma manera.

¿Cuál es la mejor manera de hacer esto, parece que pickle/json no es una opción? ¡Gracias!

actualización Leyendo la respuesta superior al How can I pickle suds results? me da una mejor idea de por qué esto no es una opción, supongo que estoy atascado recrear un objeto básico w/la información que necesito?

Respuesta

6

Sí, confirmo la explicación que di en la respuesta que se refieren a - clases generadas dinámicamente no son fácilmente estibables (ni de otro modo fácilmente serializable), es necesario extraer toda la información de estado, salmuera estado que y reconstruye el truco sudsobject en la recuperación si realmente insistes en usarlo ;-).

+5

Estoy usando la opción 'retxml = True' para recuperar el resultado sin procesar y luego convertirlo en un dict (que debería ser serializable) ahora - parece que funcionará ... ¡gracias! – jeffff

+2

@pssdbt, excelente, o podría serializar el XML en sí, pero creo que desmantelar un dict en escabeche será más rápido que analizar XML, así que creo que ha tomado la decisión correcta. –

5

Realicé una implementación de una clase ficticia para la integridad de objetos de espuma, y ​​luego pude serializar. El FakeSudsInstance se comporta como una espuma originales objeto de instancia, ver más abajo:

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) 

Ahora, después de una llamada de espuma, por ejemplo a continuación:

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

espero que ayude.

+0

'node_data.append (node_to_dict (lnode, {}))' es incorrecto ya que pasa un dict a node_to_dict. –

+0

'return node_data' dentro de' if hasattr (node, '__keylist __'): 'también debería devolver FakeSudsNode (node_data) ya que está componiendo un dict –

31

He estado usando el enfoque siguiente para convertir Suds objeto en 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

Tenga en cuenta que la función no funciona con una lista de objetos suds. Esto se puede resolver con '[recursive_asdict (d) for d in ds]'. – Jon

+2

@Jon Funciona debido a la comprobación 'elif isinstance (v, list)'. – Rafay

1

He actualizado el ejemplo recursive_asdictabove para ser compatible con python3 (items en lugar de 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

Esto es lo que se me ocurrió antes de investigar y encontrar esta respuesta. En realidad, esto funciona bien para mí en las respuestas de espuma complejas y también en otros objetos como __builtins__ ya que la solución es agnóstico espuma:

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 

Esta solución funciona y es realmente más rápido. También funciona en objetos que no tienen el atributo __keylist__.

Ejecuté un punto de referencia 100 veces en un objeto complejo de salida de espuma, el tiempo de ejecución de esta solución fue de 0.04 a 0.052 segundos (promedio 0.045724287). Mientras que la solución de recursive_asdict anterior se ejecutó en .082 a 0.102 segundos, casi el doble (promedio de 0.0829765582).

Luego volví al tablero de dibujo y volví a hacer la función para obtener un mayor rendimiento, y no necesita la importación datetime.Me apalancadas en el uso del atributo __keylist__, así que esto no va a funcionar en otros objetos tales como __builtins__, pero funciona muy bien para la espuma objeto de salida:

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 

El tiempo de ejecución fue de 0,18 - 0,033 segundos (0,0260889721 promedio), por lo que casi 4x más rápido que la solución recursive_asdict.

1

Las soluciones suggesed por encima de perder información valiosa acerca de los nombres de clase - que puede ser de valor en algunas librerías como cliente de DFP https://github.com/googleads/googleads-python-lib donde los tipos de entidades pueden ser codificados en los nombres de clase generados de forma dinámica (es decir TemplateCreative/ImageCreative)

Esta es la solución que utilicé para preservar los nombres de clase y restaurar objetos serializados sin pérdida de datos (excepto suds.sax.text.Text que se convertirían en objetos Unicode normales y tal vez algunos otros tipos en los que no me he encontrado)

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) 
Cuestiones relacionadas