2010-04-13 9 views
6

Tener un fragmento de esta manera:¿Cómo deserializar un objeto con PyYAML usando safe_load?

import yaml 
class User(object): 
    def __init__(self, name, surname): 
     self.name= name 
     self.surname= surname 

user = User('spam', 'eggs') 
serialized_user = yaml.dump(user) 
#Network 
deserialized_user = yaml.load(serialized_user) 
print "name: %s, sname: %s" % (deserialized_user.name, deserialized_user.surname) 

Yaml docs dice que no es seguro llamar yaml.load con los datos recibidos de una fuente no confiable; Entonces, ¿qué debo modificar a mi snippet \ class para usar el método safe_load?
¿Es posible?

Respuesta

9

Parece que safe_load, por definición, no te permite deserializar tus propias clases. Si usted quiere que sea seguro, me gustaría hacer algo como esto:

import yaml 
class User(object): 
    def __init__(self, name, surname): 
     self.name= name 
     self.surname= surname 

    def yaml(self): 
     return yaml.dump(self.__dict__) 

    @staticmethod 
    def load(data): 
     values = yaml.safe_load(data) 
     return User(values["name"], values["surname"]) 

user = User('spam', 'eggs') 
serialized_user = user.yaml() 
print "serialized_user: %s" % serialized_user.strip() 

#Network 
deserialized_user = User.load(serialized_user) 
print "name: %s, sname: %s" % (deserialized_user.name, deserialized_user.surname) 

La ventaja aquí es que usted tiene el control absoluto sobre la forma en que su clase es (de) serializado. Eso significa que no obtendrá código ejecutable aleatorio en la red y lo ejecutará. La desventaja es que usted tiene control absoluto sobre cómo se (de) serializa su clase. Eso significa que tienes que hacer mucho más trabajo. ;-)

+0

+1 Claro como el cristal. He visto __dict__ también para volcados con JSON. – systempuntoout

+0

¡Excelente! Básicamente, solo estás volcando el espacio de nombres de miembro, y la mejor manera de hacerlo es un dict. – Benson

16

Otra forma existe. De los documentos PyYaml:

Un objeto python se puede marcar como seguro y así ser reconocido por yaml.safe_load. Para ello, derivarlo de yaml.YAMLObject [...] y establecer explícitamente su propiedad de clase yaml_loader en yaml.SafeLoader.

También debe establecer la propiedad yaml_tag para que funcione.

YAMLObject hace algo de magia de metaclase para que el objeto sea cargable. Tenga en cuenta que si hace esto, los objetos solo serán cargados por el cargador seguro, no con el yaml.load() regular.

Ejemplo de trabajo:

import yaml 

class User(yaml.YAMLObject): 
    yaml_loader = yaml.SafeLoader 
    yaml_tag = u'!User' 

    def __init__(self, name, surname): 
     self.name= name 
     self.surname= surname 

user = User('spam', 'eggs') 
serialized_user = yaml.dump(user) 

#Network 

deserialized_user = yaml.safe_load(serialized_user) 
print "name: %s, sname: %s" % (deserialized_user.name, deserialized_user.surname) 

La ventaja de éste es que es fácil de hacer prety; Las desventajas son que solo funciona con safe_load y desordena su clase con atributos y metaclass relacionados con la serialización.

+0

buena solución. Gracias – systempuntoout

+0

esta solución es mucho más limpia. ¡¡¡Bonito!!! –

2

Si tiene muchas etiquetas y no desea crear objetos para todas ellas, o en caso de que no le importe el tipo real devuelto, solo sobre el acceso punteado, puede capturar todas las etiquetas indefinidas con el siguiente código :

import yaml 

class Blob(object): 
    def update(self, kw): 
     for k in kw: 
      setattr(self, k, kw[k]) 

from yaml.constructor import SafeConstructor 

def my_construct_undefined(self, node): 
    data = Blob() 
    yield data 
    value = self.construct_mapping(node) 
    data.update(value) 

SafeConstructor.add_constructor(None, my_construct_undefined) 


class User(object): 
    def __init__(self, name, surname): 
     self.name= name 
     self.surname= surname 

user = User('spam', 'eggs') 
serialized_user = yaml.dump(user) 
#Network 
deserialized_user = yaml.safe_load(serialized_user) 
print "name: %s, sname: %s" % (deserialized_user.name, deserialized_user.surname) 

en caso de que preguntarse por qué el my_construct_undefined tiene un yield en el medio: que permite crear instancias de objeto separado de la creación de sus hijos. Una vez que el objeto existe, se lo puede consultar en caso de que tenga un ancla y de los niños (o sus hijos) como referencia. El mecanismo real para crear el objeto primero lo crea, luego hace un next(x) para finalizarlo.

Cuestiones relacionadas