2010-01-23 6 views
6

J'ai un ensemble d'objets de transfert de données (par exemple beaucoup de requêtes, classes de message de réponse, comme MainRequest, MainResponse, ShutDownRequest, ShutDownResponse) Où de nouvelles classes continuent à apparaître lorsque le projet évolue. Ces classes doivent être (dé) sérialisées à partir de et vers différents formats XML avec différents XSD publics. De nouveaux formats XML viennent au fur et à mesure que le projet évolue.Sérialisation des objets de transfert de données dans .NET

Ma question est de savoir comment je concevoir mes classes et interfaces autour ces deux exigences, en particulier là où je devrais mettre la logique serilization réelle (de). Dois-je écrire un service statique qui peut prendre les différentes instances DTO et sait comment sérialiser chacun d'eux? Quand de nouvelles classes viennent, je dois toucher chaque FormatXSeriaizer et ajouter de nouvelles remplacements. Comme de nouveaux formats viennent, je dois juste écrire de nouvelles classes 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); 

ou si les DTO eux-mêmes savent comment le faire. Donc, j'ai tout en un seul endroit - pour chaque classe DTO. Comme de nouvelles classes DTO viennent, ils ont juste à mettre en œuvre la sérialisation pour les différents formats. Comme nouveaux formats viennent, je dois toucher chaque classe DTO.

myMainRequestInstace.Serialize(FormatTypeEnum type, XmlWriter writer); 

Ou existe-t-il une approche complètement différente? Dois-je introduire une inteface commune pour la sérialisation et avoir une inversion de contrôle, donc je pourrais charger des sérialiseurs de nouveau format lors de l'exécution?

Quel motif de conception peut me guider ici?

Quel code open source dans le monde .NET pourrais-je étudier pour voir différentes approches sur ce sujet?

EDIT: Je connais les techniques générales de sérialisation existant dans le framework. Ma question est plus orientée vers la conception de classe qui respecte les deux exigences: plusieurs formats xml et plusieurs DTO (types de message) qui continuent à venir au fur et à mesure de l'évolution du projet.

Répondre

1

Une partie de la contrepartie sera comment les différents formats XML sont. En particulier, peuvent-ils tous être accomplis en utilisant la fonctionnalité de substitution de XML Serialization? Cela vous permet de fournir un tableau d'entrées prépondérants caractéristiques comme élément ou nom d'attribut/si sérialiser comme des éléments ou des attributs, etc.

Cela pourrait vous permettre d'avoir vos différents formats ne diffèrent que du tableau de commande prioritaire est passé à le processus de sérialisation. Cela vous laisserait avec la question de savoir comment déterminer le tableau à transmettre.

+0

C'est le type de réponses que je recherche. – bitbonk

3

Il existe déjà des mécanismes intégrés pour effectuer la sérialisation xml dans .NET.

Sérialisation basée sur les attributs - overview. Tout ce que vous avez à faire est de marquer les membres de votre classe avec des attributs et d'utiliser la classe XmlSerializer pour sérialiser/désérialiser le type.


    [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" }); 

Sortie -

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

Ou vous pouvez faire toutes vos xml classes sérializable mettre en œuvre IXmlSerializable.

Modifier - Maintenant que je mal compris votre question initiale, je vais modifier la présente avec une technique que vous pouvez utiliser pour sérialiser le même objet dans plusieurs différents formats XML.

Maintenant que votre objet de transfert de données est sérialisable dans au moins un format basé sur xml, vous pouvez effectuer une étape de post-traduction pour l'obtenir dans le format souhaité en utilisant xslt transforms. Xslt est un moyen de prendre xml et de le traduire en quelque chose en utilisant un fichier xslt pour les instructions. Dans ce cas, vous traduiriez dans un autre format xml.

Voilà comment (en supposant que vous avez déjà écrit votre fichier 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

Je connais les techniques existantes dans le cadre. Ma question est plus orientée vers la conception de classe qui respecte les deux exigences: plusieurs formants xml et plusieurs DTO qui continuent à venir au fur et à mesure que le projet évolue. L'utilisation de "votre" approche ne fonctionne pas vraiment s'il existe plusieurs formats xml pour les mêmes instances DTO, ce qui est une exigence. – bitbonk

+0

Ahh je vois ce que vous dites. Je ne connais pas de moyen de sérialiser en différents formats avec le framework de sérialisation .NET xml existant mais je crois que j'ai une solution possible pour vous, j'ai donc édité ma réponse pour l'inclure. –

5

La meilleure approche serait quelque chose comme ça, voici mon approche préférée:

 
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"); 
    } 

} 

Voici comment J'applique un grain de contrôle plus serré sur la sérialisation des données binaires et augmente la version. Maintenant, ceux d'entre vous remarqueront qu'il existe déjà une fonctionnalité disponible pour cela, mais à partir de .NET 1.1, les vieilles habitudes ont la vie dure.

Notez comment dans l'exemple de code ci-dessus j'ai utilisé deux méthodes différentes HandleVersionOnePtZero et HandleVersionOnePtOne pour gérer les différentes versions du flux sérialisé. En procédant ainsi, j'ai un plus grand degré de flexibilité, disons que si le champ someField doit être changé? Notez également que le champ _fVersion est la première chose que la routine sérialisable fait d'abord, puis vérifie la version du champ et décide laquelle utiliser.

La seule chose à ce sujet, est que si vous modifiez l'espace de noms, alors vous aurez des difficultés à désérialiser les données, mais vous pouvez utiliser la classe SerializationBinder comme exemple:

 
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 

Hope this helps

+0

OOOOPPPPS Mon diable ... Quand j'écrivais ça, je ne me rendais pas compte que vous vouliez dire pour XML .... désolé pour le décousu et pour ceux qui vont downvote cela ... – t0mm13b