2010-01-23 11 views
6

Ho una serie di oggetti di dati di trasferimento (ad esempio un sacco di reqest, classi di segnalazione di risposta, come MainRequest, MainResponse, ShutDownRequest, ShutDownResponse) Dove nuove classi continuano a venire come il progetto si evolve. Queste classi devono essere (de) serializzate da e verso vari formati XML con diversi XSD pubblici. I nuovi formati XML arrivano man mano che il progetto si evolve.serializzazione Transfer Data Objects in .NET

La mia domanda qui è come vorrei progettare le mie classi e interfacce intorno questi due requisiti, soprattutto dove devo mettere la logica serilization reale (de). Devo scrivere un servizio statico che possa prendere le varie istanze DTO e sappia come serializzare ciascuna di esse? Quando arrivano nuove classi, devo toccare ogni FormatXSeriaizer e aggiungere nuove sostituzioni. Man mano che arrivano nuovi formati, devo solo scrivere nuove classi FormatXSerializer.

FormatASerializer.Serialize(XmlWriter writer, MainResponse r); 
FormatASerializer.Serialize(XmlWriter writer, ShutDownResponse r); 
FormatBSerializer.Serialize(XmlWriter writer, MainResponse r); 
FormatBSerializer.Serialize(XmlWriter writer, ShutDownResponse r); 

o gli stessi DTO dovrebbero sapere come farlo. Quindi ho tutto in un unico posto - per ogni classe DTO. Quando arrivano nuove classi DTO, devono solo implementare la serializzazione per i vari formati. Quando arrivano nuovi formati, devo toccare ogni classe DTO.

myMainRequestInstace.Serialize(FormatTypeEnum type, XmlWriter writer); 

Oppure esiste un approccio completamente diverso? Dovrei introdurre un inteface comune per la serializzazione e avere qualche inversione di controllo, quindi potrei caricare serializzatori di nuovo formato in fase di runtime?

Quale modello di progettazione può guidarmi qui?

Quale codice open source nel mondo .NET posso studiare per vedere approcci diversi su questo argomento?

MODIFICA: Conosco le tecniche di serializzazione generali esistenti nel framework. La mia domanda è più orientata verso un design di classe che rispetti i due requisiti: più formati xml e più DTO (tipi di messaggi) che continuano ad evolversi man mano che il progetto si evolve.

risposta

1

parte del corrispettivo sarà come diversi i formati XML sono. In particolare, possono essere tutti realizzati utilizzando la funzione di override della serializzazione XML? Ciò consente di fornire una serie di voci che sostituiscono funzioni come l'elemento o il nome dell'attributo/se serializzare come elementi o attributi, ecc.

Ciò potrebbe consentire di far differire i vari formati solo in base all'array di sostituzione passato a il processo di serializzazione. Questo ti lascerebbe la domanda su come determinare la matrice da passare.

+0

Questo è il tipo di risposte che sto cercando. – bitbonk

3

Esistono già alcuni meccanismi integrati per eseguire la serializzazione xml in .NET.

Serializzazione basata sugli attributi - overview. Tutto quello che devi fare è taggare i membri della tua classe con attributi e usare la classe XmlSerializer per serializzare/deserializzare il tipo.


    [XmlRoot("myXmlClass")] 
    public class MyXmlClass 
    { 
     [XmlAttribute("myAttribtue")] 
     public string MyAttribute { get; set; } 
     [XmlElement("myElement")] 
     public string MyElement { get; set; } 
    } 

    var output = new MemoryStream(); 
    var serializer = new XmlSerializer(typeof(MyXmlClass)); 
    serializer.Serialize(output, new MyXmlClass { MyAttribute = "foo", MyElement = "bar" }); 

uscita -

 
<myXmlClass myAttribute="foo"> 
    <myElement>bar</myElement> 
</myXmlClass> 

Oppure si può fare tutti i tuoi XML classi serializzabili implementano IXmlSerializable.

Modifica - Ora, poiché ho frainteso la tua domanda iniziale mi modificare il presente con una tecnica che si potrebbe usare per serializzare lo stesso oggetto in più formati XML differenti.

Ora che l'oggetto di trasferimento dati è serializzabile in almeno un formato basato su xml, è possibile eseguire un passaggio di conversione post per inserirlo nel formato desiderato utilizzando xslt transforms. Xslt è un modo per prendere xml e tradurlo in qualsiasi cosa usando un file xslt per istruzioni. In questo caso verrai tradotto in un altro formato xml.

Ecco come (supponendo che hai già scritto il file XSLT) -


    // output stream from before 
    output.Seek(0, SeekOrigin.Begin); 
    var reader = XmlReader.Create(output); 
    // cache this for performance reasons 
    XslCompiledTransform transform = new XslCompiledTransform(); 
    transform.Load("c:\myTransforms\commonToFormatA.xslt"); 
    var writer = XmlWriter.Create(Console.Out); // write final output to console. 
    transform.Transform(reader, writer); 
+0

Conosco le tecniche esistenti nel framework. La mia domanda è più orientata verso un design di classe che rispetti i due requisiti: più formanti xml e più DTO che continuano a evolversi man mano che il progetto si evolve. Anche l'utilizzo dell'approccio "tuo" non funziona se esistono più formati xml per le stesse istanze DTO, che sono un requisito. – bitbonk

+0

Ah, capisco cosa stai dicendo. Non conosco un modo per serializzare in diversi formati con il framework di serializzazione .NET xml esistente, ma credo di avere una soluzione possibile per te quindi ho modificato la mia risposta per includerla. –

5

L'approccio migliore sarebbe qualcosa di simile, questo è il mio approccio preferito:

 
public class SomeClass : ISerializable{ 
    private float _fVersion; 
    .... 
    public float Version { 
     get { return this._fVersion; } 
    } 

    private SomeClass(SerializationInfo info, StreamingContext context) { 
     bool bOk = false; 
     this._fVersion = info.GetSingle("versionID"); 
     if (this._fVersion == 1.0F) bOk = this.HandleVersionOnePtZero(info, context); 
     if (this._fVersion == 1.1F) bOk = this.HandleVersionOnePtOne(info, context); 

     if (!bOk) throw new SerializationException(string.Format("SomeClass: Could not handle this version {0}.", this._fVersion.ToString())); 
    } 
    public void GetObjectData(SerializationInfo info, StreamingContext context) { 
     info.AddValue("versionID", this._fVersion); 
     if (this._fVersion == 1.0F) { 
     info.AddValue("someField", ...); 
     info.AddValue("anotherField", ...); 
     } 
     if (this._fVersion == 1.1F) { 
     info.AddValue("someField1", ...); 
     info.AddValue("anotherField2", ...); 
     } 
    } 
    private bool HandleVersionOnePtZero(SerializationInfo info, StreamingContext context) { 
     bool rvOk = false; 
     ... = info.GetValue("someField"); 
     ... = info.GetValue("anotherField"); 
    } 

    private bool HandleVersionOnePtOne(SerializationInfo info, StreamingContext context) { 
     bool rvOk = false; 
     ... = info.GetValue("someField1"); 
     ... = info.GetValue("anotherField2"); 
    } 

} 

Questo è il modo in Applico un controllo più severo sulla serializzazione dei dati binari e aumento la versione. Ora, quelli di voi faranno notare che esiste già una funzionalità disponibile per farlo, ma che proviene da .NET 1.1, beh, le vecchie abitudini sono dure a morire.

Nota come nell'esempio di codice precedente ho utilizzato due metodi diversi HandleVersionOnePtZero e HandleVersionOnePtOne per gestire le diverse versioni del flusso serializzato. In questo modo, ho un maggiore grado di flessibilità, ad esempio se il campo someField deve essere modificato? Inoltre, nota come il campo _fVersion è la prima cosa che fa la routine serializzabile, quindi controlla la versione del campo e decide quale utilizzare.

L'unica cosa di questo, è se si cambia lo spazio dei nomi, allora si avrà difficoltà a deserializzazione dei dati, ma è possibile utilizzare la classe SerializationBinder come esempio:

 
public class SomeClassBinder : System.Runtime.Serialization.SerializationBinder { 
    public override Type BindToType(string assemblyName, string typeName) { 
     Type typeToDeserialize = null; 
     try { 
     // For each assemblyName/typeName that you want to deserialize to 
     // a different type, set typeToDeserialize to the desired type. 
     string assemVer1 = System.Reflection.Assembly.GetExecutingAssembly().FullName; 
     if (assemblyName.StartsWith("foobar")) { 
      assemblyName = assemVer1; 
      typeName = "fubar" + typeName.Substring(typeName.LastIndexOf("."), (typeName.Length - typeName.LastIndexOf("."))); 
     } 
     typeToDeserialize = Type.GetType(String.Format("{0}, {1}", typeName, assemblyName)); 
     } catch (System.Exception ex1) { 
      throw ex1; 
     } finally { 
    } 
    return typeToDeserialize; 
    } 
} 

it would be called like this: 
BinaryFormatter bf = new BinaryFormatter(); 
bf.Binder = new SomeClassBinder(); 
SomeClass sc = bf.Deserialize(stream); // assume stream is declared and open 

Spero che questo aiuti

+0

OOOOPPPPS Il mio male ... Quando stavo scrivendo questo, non mi ero reso conto che intendevi per XML .... scusami per il vagabondaggio e per quelli che lo sottovaluteranno ... – t0mm13b