2009-12-30 5 views
7

J'essaie de sérialiser une classe qui hérite d'une classe de base qui implémente IXmlSerializable.Comment revenir à la sérialisation XML 'par défaut' lors de l'implémentation de IXmlSerializable dans une classe de base?

La classe de base, appelée PropertyBag est une classe qui autorise les propriétés dynamiques (credits to Marc Gravell).

J'ai implémenté IXmlSerializable de sorte que les propriétés dynamiques (stockées dans un dictionnaire) soient écrites en tant qu'éléments xml normaux.

par exemple. Lors de la sérialisation une personne de classe avec une propriété publique (non dynamique) Nom et âge de la propriété dynamique, je voudrais que pour générer le code XML suivant:

<Person> 
    <Name>Tim</Name> 
    <DynamicProperties> 
    <Country> 
     <string>USA</string> 
    </Country> 
    </DynamicProperties> 
<Person> 

je peux obtenir la pièce à travailler avec la mise en œuvre suivante de WriteXml dans la classe de base PropertyBag:

public void WriteXml(System.Xml.XmlWriter writer) 
    { 
     writer.WriteStartElement("DynamicProperties"); 

     // serialize every dynamic property and add it to the parent writer 
     foreach (KeyValuePair<string, object> kvp in properties) 
     { 
      writer.WriteStartElement(kvp.Key); 

      StringBuilder itemXml = new StringBuilder(); 
      using (XmlWriter itemWriter = XmlWriter.Create(itemXml)) 
      { 
       // serialize the item 
       XmlSerializer xmlSer = new XmlSerializer(kvp.Value.GetType()); 
       xmlSer.Serialize(itemWriter, kvp.Value);      

       // read in the serialized xml 
       XmlDocument doc = new XmlDocument(); 
       doc.LoadXml(itemXml.ToString()); 

       // write to modified content to the parent writer 
       writer.WriteRaw(doc.DocumentElement.OuterXml); 
      } 

      writer.WriteEndElement(); 
     } 

     writer.WriteEndElement(); 
    } 

Cependant, lors de la sérialisation la classe personne, il ne sérialise les propriétés normales (non dynamique) à moins que j'Ecraser la méthode WriteXml en personne (que je ne veux pas faire). Est-il possible que dans la classe de base, je peux ajouter automatiquement les propriétés statiques? Je sais que je peux le faire manuellement en utilisant la réflexion, mais je me demandais s'il y a une fonctionnalité intégrée dans le. Net Framework?

+0

Je pense que vous devriez éviter le mot "statique", car cela a une autre signification (très différente) ... –

Répondre

1

Marc, votre réponse à mettre les FixedProperties dans une séparée collection m'a fait penser qu'au lieu d'hériter à partir de PropertyBag, je devrais créer une propriété de ce type. J'ai donc créé une classe PropertyBagWrapper dont hérite ma classe Person et ça marche.

[Serializable] 
[TypeDescriptionProvider(typeof(PropertyBagDescriptionProvider))]  
public abstract class PropertyBagWrapper 
{ 
    [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]  
    public PropertyBag DynamicProperties { get; set; } 

    public object this[string name] 
    { 
     get { return DynamicProperties[name]; } 
     set { DynamicProperties[name] = value; } 
    } 
    protected PropertyBagWrapper() 
    { 
     DynamicProperties = new PropertyBag(this.GetType()); 
    } 
} 

[Serializable]  
public class Person : PropertyBagWrapper 
{ 
    [Browsable(true)] 
    public string Name { get; set; } 
} 

Je ne vais pas répéter tout le code pour le PropertyBag et les classes personnalisées nécessaires à la mise en œuvre ICustomTypeDescriptor, vous pouvez constater que here.

J'ai déplacé l'attribut TypeDescriptionProvider de la classe PropertyBag vers la classe PropertyBagWrapper.

La classe PropertyBag a toujours la même implémentation pour la méthode WriteXml() que celle indiquée dans la question.

3

J'ai passé beaucoup de temps avec XmlSerializer (et diverses autres API de sérialisation), et je suis assez sûr que tout simplement: vous ne pouvez pas. La mise en œuvre IXmlSerializable est tout ou rien. Le plus proche que je puisse penser est de tricher et de déplacer toutes les propriétés fixes vers un sous-objet; cela vous donnerait un peu différent xml - quelque chose comme:

<FixedProperties> 
    <Name>Tim</Name> 
</FixedProperties> 
<DynamicProperties> 
    <Country> 
    <string>USA</string> 
    </Country> 
</DynamicProperties> 

mais je m'attends à ce que cela fonctionne. Vous auriez des propriétés pass-thru sur votre objet de base:

[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] 
public FixedProperties FixedProps {get;set;} 
public string Name { 
    get {return FixedProps.Name;} 
    set {FixedProps.Name = value;} 
} 

Vous avez du sens? Vous pouvez également marquer Name comme [XmlIgnore], mais cela semble assez redondant. Dans la méthode serialize sur mesure vous utiliseriez new XmlSerializer(typeof(FixedProperties))

Edit: Voici un exemple de travail « serialize »:

using System; 
using System.ComponentModel; 
using System.Xml.Serialization; 

static class Program 
{ 
    static void Main() 
    { 
     MyType obj = new MyType { Name = "Fred" }; 
     var ser = new XmlSerializer(obj.GetType()); 
     ser.Serialize(Console.Out, obj); 
    } 
} 
public class MyType : IXmlSerializable 
{ 
    public MyType() 
    { 
     FixedProperties = new MyTypeFixedProperties(); 
    } 
    [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] 
    public MyTypeFixedProperties FixedProperties { get; set; } 
    [XmlIgnore] 
    public string Name 
    { 
     get { return FixedProperties.Name; } 
     set { FixedProperties.Name = value; } 
    } 

    System.Xml.Schema.XmlSchema IXmlSerializable.GetSchema() 
    { 
     return null; 
    } 

    void IXmlSerializable.ReadXml(System.Xml.XmlReader reader) 
    { 
     throw new System.NotImplementedException(); 
    } 

    void IXmlSerializable.WriteXml(System.Xml.XmlWriter writer) 
    { 
     writer.WriteStartElement("DynamicProperties"); 
     writer.WriteElementString("Foo", "Bar"); 
     writer.WriteEndElement(); 
     fixedPropsSerializer.Serialize(writer, FixedProperties); 
    } 
    static readonly XmlSerializer fixedPropsSerializer 
     = new XmlSerializer(typeof(MyTypeFixedProperties)); 

} 
[XmlRoot("FixedProperties")] 
public class MyTypeFixedProperties 
{ 
    public string Name { get; set; } 
} 
+0

Droit sur l'argent. C'est pourquoi le contenu de DataContract a été ajouté dans les versions plus récentes de .Net. C'est pourquoi les "anciens" services web asmx soufflent aussi, parce que vous ne pouvez pas détecter la sérialisation de vos objets sans écrire complètement la sérialisation à partir de zéro. – jvenema

+0

Cela devrait fonctionner, mais pas pour mon but prévu. Les classes dérivées existent déjà, je voulais juste ajouter des fonctionnalités pour les propriétés dynamiques. Les propriétés fixes doivent rester dans la classe dérivée. J'apprécie énormément la contribution cependant! – Timmel

+0

@jvenema - Serait-ce possible en utilisant les attributs DataContract alors et si oui, avez-vous de bons pointeurs pour moi? – Timmel