2010-04-13 5 views
6

einen Ausschnitt wie folgt zu haben:Wie wird ein Objekt mit PyYAML mit safe_load deserialisiert?

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 sagt, dass es nicht sicher ist yaml.load mit beliebigen Daten aus einer nicht vertrauenswürdigen Quelle erhalten zu nennen; Also, was sollte ich ändern, um meine Snippet \ Klasse zu verwenden safe_load Methode?
Ist es möglich?

Antwort

9

Es scheint, dass safe_load per Definition die eigenen Klassen nicht deserialisieren kann. Wenn Sie es sicher sein wollen, würde ich so etwas tun:

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) 

Der Vorteil hierbei ist, dass Sie absolute Kontrolle darüber haben, wie Sie Ihre Klasse (de) serialisiert. Das bedeutet, dass Sie keinen zufälligen ausführbaren Code über das Netzwerk erhalten und ausführen. Der Nachteil ist, dass Sie absolute Kontrolle darüber haben, wie Ihre Klasse serialisiert wird. Das heißt, Sie müssen viel mehr arbeiten. ;-)

+0

+1 Kristallklar. Ich habe __dict__ auch für Dumps mit JSON gesehen. – systempuntoout

+0

Ausgezeichnet! Im Grunde wird nur der Member-Namespace gedumpt und der beste Weg dazu ist ein Diktat. – Benson

16

Ein anderer Weg existiert. Aus der PyYaml-Dokumentation:

Ein Python-Objekt kann als sicher markiert und somit von yaml.safe_load erkannt werden. Um dies zu tun, leite es von yaml.YAMLObject [...] ab und setze seine Klasseneigenschaft yaml_loader explizit auf yaml.SafeLoader.

Sie müssen auch die yaml_tag-Eigenschaft festlegen, damit es funktioniert.

YAMLObject verwendet einige Metaklassen, um das Objekt ladbar zu machen. Beachten Sie, dass die Objekte nur dann vom sicheren Loader geladen werden können, nicht mit dem regulären yaml.load().

Arbeitsbeispiel:

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) 

Der Vorteil dieser ist, dass es prety einfach zu tun; Die Nachteile sind, dass es nur mit safe_load funktioniert und die Klasse mit serialisierungsbezogenen Attributen und Metaklasse überfüllt.

+0

nice solution.Thanks – systempuntoout

+0

diese Lösung ist viel sauberer. Nett!!! –

2

Wenn Sie viele Tags haben und keine Objekte für alle erstellen möchten, oder wenn Sie sich nicht für den zurückgegebenen tatsächlichen Typ interessieren, nur für den punktierten Zugriff, fangen Sie alle nicht definierten Tags mit dem folgenden Code ab :

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) 

Falls Sie sich fragen, warum die my_construct_undefined eine yield in der Mitte hat: die von der Erstellung ihrer Kinder zum Instanziieren des Objekts getrennt werden können. Sobald das Objekt existiert, kann Bezug genommen werden, falls es einen Anker hat, und auf die Kinder (oder ihre Kinder) eine Referenz. Der eigentliche Mechanismus, um das Objekt zu erstellen, erstellt es zuerst, dann macht es eine next(x) um es zu finalisieren.

Verwandte Themen