2010-04-13 12 views
6

Avere un frammento come questo:Come deserializzare un oggetto 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 che non è sicuro chiamare yaml.load con tutti i dati ricevuti da una fonte non attendibile; quindi, cosa devo modificare nel mio snippet \ class per utilizzare il metodo safe_load?
È possibile?

risposta

9

Sembra che safe_load, per definizione, non ti consenta di deserializzare le tue classi. Se si vuole che sia al sicuro, mi piacerebbe fare qualcosa di simile:

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) 

Il vantaggio è che si ha il controllo assoluto su come la vostra classe è (de) serializzato. Ciò significa che non si otterrà il codice eseguibile casuale sulla rete ed eseguirlo. Lo svantaggio è che hai il controllo assoluto su come la tua classe è (de) serializzata. Ciò significa che devi fare molto più lavoro. ;-)

+0

+1 Crystal Clear. Ho visto __dict__ anche per i dump con JSON. – systempuntoout

+0

Eccellente! Fondamentalmente stai solo scaricando il namespace dei membri, e il modo migliore per farlo è un dittico. – Benson

16

Un altro modo esiste. Da PyYaml docs:

Un oggetto python può essere contrassegnato come sicuro e quindi riconosciuto da yaml.safe_load. Per fare ciò, derivarlo da yaml.YAMLObject [...] e impostare esplicitamente la proprietà della classe yaml_loader su yaml.SafeLoader.

È inoltre necessario impostare la proprietà yaml_tag per farlo funzionare.

YAMLObject esegue una metaclasse magica per rendere l'oggetto caricabile. Notare che se si esegue questa operazione, gli oggetti saranno caricabili solo dal caricatore sicuro, non con il normale yaml.load().

esempio di lavoro:

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) 

Il vantaggio di questo è che è prety facile da fare; gli svantaggi sono che funziona solo con safe_load e ingombra la classe con attributi e metaclassi relativi alla serializzazione.

+0

nice solution.Thanks – systempuntoout

+0

questa soluzione è molto più pulita. Bello!!! –

2

Se si hanno molti tag e non si desidera creare oggetti per tutti loro, o nel caso in cui non si preoccupi del tipo effettivo restituito, solo sull'accesso puntato, si catturano tutti i tag non definiti con il seguente codice :

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) 

nel caso in cui chiedersi perché il my_construct_undefined ha un yield in mezzo: che permette di istanziare l'oggetto separatamente dalla creazione dei suoi figli. Una volta che l'oggetto esiste, può essere indicato nel caso in cui abbia un'ancora e dei bambini (o dei loro figli) un riferimento. Il meccanismo attuale per creare l'oggetto prima lo crea, quindi fa un next(x) su di esso per finalizzarlo.

Problemi correlati