Cómo deserializar un objeto con PyYAML usando safe_load?
-
26-09-2019 - |
Pregunta
Tener un fragmento de esta manera:
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 a yaml.load con cualquier dato recibido de una fuente no confiable; así, ¿cuál debería modificar a mi fragmento \ clase que se utiliza safe_load método?
¿Es posible?
Solución
Parece que safe_load, por definición, no deje que deserializar sus 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 usted no conseguirá código ejecutable al azar través de la red y ejecutarlo. La desventaja es que usted tiene el control absoluto sobre la forma en que su clase es (de) serializado. Eso significa que tiene que hacer mucho más trabajo. ; -)
Otros consejos
Existe otra manera. A partir de los documentos PyYAML:
Un objeto pitón puede ser marcados como seguros y por lo tanto ser reconocido por yaml.safe_load. Para ello, derivarla de yaml.YAMLObject [...] y establecer explícitamente su yaml_loader propiedad de clase a yaml.SafeLoader.
También tiene que establecer la propiedad yaml_tag para hacer que funcione.
YAMLObject hace un poco de magia para hacer que la metaclase cargable objeto. Tenga en cuenta que si lo hace, los objetos serán sólo se puede cargar por el cargador seguro, no con 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 sólo funciona con safe_load y estorba su clase con atributos y metaclase relacionados serialización.
Si usted tiene muchas etiquetas y no desea crear objetos para todos ellos, o en caso de que no se preocupan por el tipo real regresó, sólo el acceso de puntos, que captura todas las etiquetas definidas por el código siguiente:
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 una yield
en el medio: que permite crear instancias de objeto separado de la creación de sus hijos. Una vez que el objeto existe puede ser mencionado en el caso de que tenga un ancla y de los niños (o sus hijos) una referencia. El mechanisme real para crear el objeto crea en primer lugar, y luego hace un next(x)
en él para finalizarlo.