Como anular a serialização de um objeto com PyYAML usando safe_load?
-
26-09-2019 - |
Pergunta
Ter um trecho como este:
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 diz que não é seguro chamar yaml.carga com os dados recebidos a partir de uma fonte não confiável;então, o que devo modificar meus trecho\classe para usar safe_load método?
É possível?
Solução
Parece que Safe_load, por definição, não permite que você desserialize suas próprias classes. Se você quiser que seja seguro, eu faria algo assim:
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)
A vantagem aqui é que você tem controle absoluto sobre como sua classe é (de) serializada. Isso significa que você não obterá código executável aleatório pela rede e executá -lo. A desvantagem é que você tem controle absoluto sobre como sua classe é (de) serializada. Isso significa que você precisa fazer muito mais trabalho. ;-)
Outras dicas
Outra maneira de existir.A partir do PyYaml docs:
Um objeto python podem ser marcados como seguros e, assim, ser reconhecido pelo yaml.safe_load.Para fazer isso, derivar-lo a partir de yaml.YAMLObject [...] e explicitamente defina sua propriedade de classe yaml_loader para yaml.SafeLoader.
Você também precisa definir o yaml_tag propriedade para fazê-lo funcionar.
YAMLObject faz algum metaclasse magia para fazer o objeto carregável.Observe que, se você fizer isso, os objetos só serão carregável pelo seguro carregador, não regular yaml.load().
Exemplo de trabalho:
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)
A vantagem desta é que é prety fácil de fazer;as desvantagens são que ele só funciona com safe_load e clutters sua classe com a serialização de atributos relacionados e metaclasse.
Se você tem muitas tags e não deseja criar objetos para todos eles, ou caso não se importe com o tipo real retornado, apenas sobre acesso pontilhado, você pega todas as tags indefinidas com o seguinte 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)
Caso você se pergunte por que o my_construct_undefined
tem um yield
No meio: isso permite instantar o objeto separadamente da criação de seus filhos. Uma vez que o objeto exista, ele pode ser referido, caso tenha uma âncora e das crianças (ou seus filhos) uma referência. O mecanismo real para criar o objeto primeiro o cria, depois faz um next(x)
nele para finalizá -lo.