2012-10-31 9 views
20

Sto usando BinaryFormatter per eseguire la serializzazione binaria di alcuni oggetti in C#. Tuttavia, alcuni degli oggetti contengono classi a cui accedo tramite una DLL e non ho il codice sorgente, quindi non posso contrassegnarli con l'attributo Serializable. C'è un modo semplice per serializzarli comunque? Ho una soluzione alternativa che implica prendere la classe NoSource e creare una nuova classe SerializableNoSource per la quale il costruttore prende un oggetto NoSource ed estrae tutte le informazioni necessarie, ma è un hacker. Ci sono alternative migliori?È possibile eseguire la serializzazione binaria .NET di un oggetto quando non si dispone del codice sorgente della classe?

+7

Per la registrazione, incapsulare l'oggetto in un altro oggetto che * non * sa come serializzare se stesso così come la conversione da/verso l'oggetto incapsulato non è hacky nella mia mente, è del tutto appropriato. Ora, potrebbe esserci qualcosa di più sottile o meglio là fuori, ma non considererei il tuo approccio attuale fondamentalmente difettoso. – Servy

risposta

-2

Penso che il modo più semplice sarebbe quello di integrare Interfaccia ISerializable e gestire autonomamente la serializzazione e il processo inverso. In MSDN possiamo trovare:

serializzazione non può essere aggiunto a una classe dopo che è stato compilato ....

+0

"Tuttavia, alcuni degli oggetti contengono classi che accedo tramite una DLL e non hanno il codice sorgente per" –

+0

Puoi pensare di creare la tua classe che potrebbe avere le stesse proprietà dell'oggetto che non hai la fonte No ? O lo estende ?? –

+0

Estendere non funziona, copiare le proprietà ... è lo stesso di quello che ha ora –

1

Potreste essere in grado di utilizzare Mono.Cecil per aggiungere il [SerializableAttribute] alle classi , ma non lo farei se c'è un altro modo per ottenere il risultato desiderato.

0

Sono d'accordo con @Servy, se l'autore della classe non ha previsto che sarebbe stato serializzato, non si dovrebbe tentare di serializzare direttamente. Quindi stai facendo la cosa giusta da un punto di vista architettonico. Per rendere meno attuale il tuo approccio, "hacky", considera l'implementazione di ISerializable per le classi che contengono riferimenti a oggetti non serializzabili.

0

Creare una nuova classe, ereditare la classe esistente che non è contrassegnata con l'attributo di serializzazione e implementare l'interfaccia ISerializable.

Se la classe è sigillata, è possibile utilizzare Json.NET e quindi convertirlo in binario e viceversa (succhia grande tempo, usarlo se non altro può aiutare :)).

20

È possibile creare un surrogato di serializzazione .

Immaginiamo che abbiamo una classe definita in un assembly di riferimento che non abbiamo alcun controllo su che assomiglia a questo:

public class Person 
{ 
    public string Name { get; set; } 
    public int Age { get; set; } 
    public DriversLicense License; 
} 


// An instance of this type will be part of the object graph and will need to be 
// serialized also. 
public class DriversLicense 
{ 
    public string Number { get; set; } 
} 

Per serializzare questo oggetto è necessario definire un surrogato di serializzazione per ogni tipo nel grafico dell'oggetto.

Per creare un surrogato di serializzazione è sufficiente creare un tipo che implementa l'interfaccia ISerializationSurrogate:

public class PersonSurrogate : ISerializationSurrogate 
{ 
    /// <summary> 
    /// Manually add objects to the <see cref="SerializationInfo"/> store. 
    /// </summary> 
    public void GetObjectData(object obj, SerializationInfo info, StreamingContext context) 
    { 
     Person person = (Person) obj; 
     info.AddValue("Name", person.Name); 
     info.AddValue("Age", person.Age); 
     info.AddValue("License", person.License); 
    } 

    /// <summary> 
    /// Retrieves objects from the <see cref="SerializationInfo"/> store. 
    /// </summary> 
    /// <returns></returns> 
    public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector) 
    { 
     Person person = (Person)obj; 
     person.Name = info.GetString("Name"); 
     person.Age = info.GetInt32("Age"); 
     person.License = (DriversLicense) info.GetValue("License", typeof(DriversLicense)); 
     return person; 
    } 
} 

public class DriversLicenseSurrogate : ISerializationSurrogate 
{ 
    /// <summary> 
    /// Manually add objects to the <see cref="SerializationInfo"/> store. 
    /// </summary> 
    public void GetObjectData(object obj, SerializationInfo info, StreamingContext context) 
    { 
     DriversLicense license = (DriversLicense)obj; 
     info.AddValue("Number", license.Number); 
    } 

    /// <summary> 
    /// Retrieves objects from the <see cref="SerializationInfo"/> store. 
    /// </summary> 
    /// <returns></returns> 
    public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector) 
    { 
     DriversLicense license = (DriversLicense)obj; 
     license.Number = info.GetString("Number"); 
     return license; 
    } 
} 

Allora avete bisogno di lasciare il vostro IFormatter conoscono i surrogati attraverso la definizione e l'inizializzazione di una SurrogateSelector e assegnandolo a il tuo IFormatter.

private static void SerializePerson(Person person) 
{ 
    if (person == null) 
     throw new ArgumentNullException("person"); 

    using (var memoryStream = new MemoryStream()) 
    { 
     //Configure our surrogate selectors. 
     var surrogateSelector = new SurrogateSelector(); 
     surrogateSelector.AddSurrogate(typeof (Person), new StreamingContext(StreamingContextStates.All), 
             new PersonSurrogate()); 
     surrogateSelector.AddSurrogate(typeof (DriversLicense), new StreamingContext(StreamingContextStates.All), 
             new DriversLicenseSurrogate()); 

     //Serialize the object 
     IFormatter formatter = new BinaryFormatter(); 
     formatter.SurrogateSelector = surrogateSelector; 
     formatter.Serialize(memoryStream, person); 

     //Return to the beginning of the stream 
     memoryStream.Seek(0, SeekOrigin.Begin); 

     //Deserialize the object 
     Person deserializedPerson = (Person) formatter.Deserialize(memoryStream); 
    } 
} 

Utilizzando un surrogato di serializzazione non è affatto semplice, e può effettivamente diventare molto verboso quando il tipo si sta cercando di serializzare ha & campi privati ​​protetti che devono essere serializzato.

Tuttavia, poiché la serializzazione manuale dei valori è necessaria, non penso che sia un problema.L'uso di un surrogato è un modo più unifom di gestire uno scenario come questo e dovrebbe farti sentire più a tuo agio.

+0

Questa sembra essere la risposta da manuale per serializzare veramente una classe sconosciuta che non è contrassegnata '[Serializable]' –

+0

Se vuoi maggiori informazioni puoi leggere il capitolo 23 di [CLR via C#] (http://www.amazon.co.uk/CLR-via-C-Jeffrey-Richter/dp/0735627045) - Serializzazione runtime. –

+0

Trova inoltre questo articolo pertinente su MSDN: http://msdn.microsoft.com/en-us/magazine/cc188950.aspx –

Problemi correlati