2009-07-04 8 views
7
public class Options 
    { 
     public FolderOption FolderOption { set; get; } 

     public Options() 
     { 
      FolderOption = new FolderOption(); 
     } 


     public void Save() 
     { 
      XmlSerializer serializer = new XmlSerializer(typeof(Options)); 
      TextWriter textWriter = new StreamWriter(@"C:\Options.xml"); 
      serializer.Serialize(textWriter, this); 
      textWriter.Close(); 
     } 

     public void Read() 
     { 
      XmlSerializer deserializer = new XmlSerializer(typeof(Options)); 
      TextReader textReader = new StreamReader(@"C:\Options.xml"); 
      //this = (Options)deserializer.Deserialize(textReader); 
      textReader.Close(); 

     } 
    } 
} 

J'ai réussi à enregistrer sans problème, tous les membres de FolderOption sont désérialisés. Mais le problème est de savoir comment le lire? La ligne - // this = (Options) deserializer.Deserialize (textReader); ne fonctionnera pas.C# - Comment xml désérialiser objet lui-même?

Modifier: Une solution à ce problème? Pouvons-nous atteindre le même but sans y assigner? Cela désérialise l'objet Options dans l'option. Je suis fainéant de le faire par propriété. Effectuer au plus haut niveau permettrait d'économiser beaucoup d'efforts.

Répondre

11

Cela fonctionnera si votre type d'options est struct, car vous pouvez modifier une structure elle-même.

Si Options est une classe (type de référence), vous ne pouvez pas affecter à l'instance actuelle d'un type de référence dans cette instance. suggérez-vous pour écrire une classe d'aide, et mettre vos méthodes de lecture et sauvegardez.y, comme celui-ci

 public class XmlSerializerHelper<T> 
    { 
     public Type _type; 

     public XmlSerializerHelper() 
     { 
      _type = typeof(T); 
     } 


     public void Save(string path, object obj) 
     { 
      using (TextWriter textWriter = new StreamWriter(path)) 
      { 
       XmlSerializer serializer = new XmlSerializer(_type); 
       serializer.Serialize(textWriter, obj); 
      } 

     } 

     public T Read(string path) 
     { 
      T result; 
      using (TextReader textReader = new StreamReader(path)) 
      { 
       XmlSerializer deserializer = new XmlSerializer(_type); 
       result = (T)deserializer.Deserialize(textReader); 
      } 
      return result; 

     } 
    } 

et consomment ensuite de votre interlocuteur, de lire et enregistrer des objets, au lieu d'essayer de la classe.

//In the caller 

var helper=new XmlSerializerHelper<Options>(); 
var obj=new Options(); 

//Write and read 
helper.Save("yourpath",obj); 
obj=helper.Read("yourpath"); 

Et mettre le XmlSerializerHelper dans votre espace de noms de Util, il est réutilisable et fonctionnera avec tout type.

+0

-1 pour ne pas implémenter les blocs "using", et pour ne pas utiliser de génériques. –

+0

using (XmlSerializer deserializer = new XmlSerializer (_type)) ne fonctionne pas. XmlSerializer n'a pas implémenté IDisposable. La bonne devrait ressembler à celle de John, mettant XmlSerializer en dehors du bloc using. –

+0

Oups, oublié un instant que XmlSerializer n'a pas implémenté IDisposable, corrigé :) – amazedsaint

0

Voir XmlSerializer.Deserialize Method: Vous pouvez créer une méthode statique comme ce qui suit:

public static Options DeserializeFromFile(string filename) {  
     // Create an instance of the XmlSerializer specifying type and namespace. 
     XmlSerializer serializer = new XmlSerializer(typeof(Options)); 

     // A FileStream is needed to read the XML document. 
     using (FileStream fs = new FileStream(filename, FileMode.Open)) { 
      XmlReader reader = new XmlTextReader(fs); 
      return (Options) serializer.Deserialize(reader); 
     } // using 
    } 

Le ci-dessus peut être appelé:

Options foo = Options.DeserializeFromFile(@"C:\Options.xml"); 
+0

1) Veuillez lire sa question plus attentivement. 2) -1 pour ne pas utiliser les blocs "using". –

+0

Vous avez raison, je devrais. –

+0

Vous avez toujours besoin d'une utilisation autour du XmlReader. –

5

Un objet ne peut se désérialiser, par définition: il existe déjà et la désérialisation crée une nouvelle instance du type.

Il est parfois judicieux de créer une nouvelle instance vide d'une classe, puis de la remplir avec des informations provenant de XML. L'instance pourrait également être "presque vide". Vous pouvez le faire, par exemple, pour charger les préférences de l'utilisateur ou, en général, pour rétablir l'instance telle qu'elle était auparavant. L'état "vide" ou "presque vide" de l'instance serait un état valide pour la classe: elle ne connaîtrait pas l'état dans lequel elle se trouvait avant sa persistance.


Aussi, je vous conseille de prendre l'habitude de mettre en œuvre « en utilisant » blocs:

public void Save() 
{ 
    XmlSerializer serializer = new XmlSerializer(typeof(Options)); 
    using (TextWriter textWriter = new StreamWriter(@"C:\Options.xml")) 
    { 
     serializer.Serialize(textWriter, this); 
     // no longer needed: textWriter.Close(); 
    } 
} 

public void Read() 
{ 
    XmlSerializer deserializer = new XmlSerializer(typeof(Options)); 
    using (TextReader textReader = new StreamReader(@"C:\Options.xml")) 
    { 
     // no longer needed: textReader.Close(); 
    } 
} 

Cela garantira que les TextReaders sont éliminés, même si une exception est levée. C'est pourquoi les appels Close ne sont plus nécessaires.

+1

Bon rappel, va changer en utilisant le bloc. –

18

Construisez votre méthode .Read() en fonction statique qui retourne l'objet de lecture:

public static Options Read(string path) 
{ 
    XmlSerializer deserializer = new XmlSerializer(typeof(Options)); 
    using (TextReader textReader = new StreamReader(path)) 
    { 
     return (Options)deserializer.Deserialize(textReader); 
    } 
} 

Ensuite, changez votre code d'appel alors plutôt que quelque chose comme ceci:

Options myOptions = new Options(); 
myOptions.Read(@"C:\Options.xml"); 

Vous faites quelque chose comme ceci:

Options myOptions = Options.Read(@"C:\Options.xml"); 

La belle différence est que c'est impossible Impossible d'avoir un objet Options qui n'a pas de données derrière lui.

+0

Je suis d'accord, cet IMO est la solution la plus propre qui répond à la question. –

+0

Je ne peux pas commenter pour les autres, mais cela correspond mieux à mon projet actuel ... très soigné. – CJM

1

Je suis allé à cette approche (en vb)

Public Class SerialisableClass 

    Public Sub SaveToXML(ByVal outputFilename As String) 

     Dim xmls = New System.Xml.Serialization.XmlSerializer(Me.GetType) 
     Using sw = New IO.StreamWriter(outputFilename) 
      xmls.Serialize(sw, Me) 
     End Using 

    End Sub 

    Private tempState As Object = Me 
    Public Sub ReadFromXML(ByVal inputFilename As String) 

     Dim xmls = New System.Xml.Serialization.XmlSerializer(Me.GetType) 

     Using sr As New IO.StreamReader(inputFilename) 
      tempState = xmls.Deserialize(sr) 
     End Using 

     For Each pi In tempState.GetType.GetProperties() 

      Dim name = pi.Name 

      Dim realProp = (From p In Me.GetType.GetProperties 
          Where p.Name = name And p.MemberType = Reflection.MemberTypes.Property).Take(1)(0) 

      realProp.SetValue(Me, pi.GetValue(tempState, Nothing), Nothing) 

     Next 

    End Sub 

End Class 

je peux alors utiliser simplement quelque chose comme ceci:

Public Class ClientSettings 

    Inherits SerialisableClass 

    Public Property ZipExePath As String 
    Public Property DownloadPath As String 
    Public Property UpdateInstallPath As String 

End Class 

et l'appeler comme ceci:

Dim cs As New ClientSettings 
cs.ReadFromXML("c:\myXMLfile.xml") 

ou encore mieux (si j'ajoute le constructeur nécessaire):

Dim cs as New ClientSettings("c:\myXMLFile.xml") 

Cela me semble agréable et propre et fonctionne bien dans ma situation.

Vive

2

Je pense que la manière simpliest sérialiser et désérialiser un objet est d'utiliser une classe statique avec les deux méthodes suivantes. Nous avons également besoin d'une classe nommée StringWriterWithEncoding pour définir le codage de la chaîne XML, car la propriété Encoding de la classe standard StringWriter est en lecture seule.(Trouvé ici: http://devproj20.blogspot.com/2008/02/writing-xml-with-utf-8-encoding-using.html)

public static class GenericXmlSerializer 
{ 
    public static string Serialize<T>(T obj, Encoding encoding) 
    { 
     XmlSerializer serializer = new XmlSerializer(typeof(T));    
     TextWriter textWriter = new StringWriterWithEncoding(new StringBuilder(), encoding); 
     serializer.Serialize(textWriter, obj); 

     return textWriter.ToString(); 
    } 

    public static T Deserialize<T>(string xml) 
    { 
     XmlSerializer serializer = new XmlSerializer(typeof(T)); 
     TextReader textReader = new StringReader(xml); 
     return (T)serializer.Deserialize(textReader); 
    } 
} 

public class StringWriterWithEncoding : StringWriter 
{ 
    Encoding encoding; 

    public StringWriterWithEncoding(StringBuilder builder, Encoding encoding) 
     : base(builder) 
    { 
     this.encoding = encoding; 
    } 

    public override Encoding Encoding 
    { 
     get { return encoding; } 
    } 
} 

Utilisation:

//serialize 
MyClass myClass = new MyClass(); 
string xml = GenericXmlSerializer.Serialize<MyClass>(myClass, Encoding.Unicode); 

//deserialize 
MyClass myClass2 = GenericXmlSerializer.Deserialize<MyClass>(xml); 
2

Je suis fan de méthodes d'extension, donc j'utiliser ce toujours:

using System.IO; 
using System.Xml.Serialization; 

public static class SerializationExtensionMethods 
{ 
    /// <summary> 
    /// Serializes the object. 
    /// </summary> 
    /// <typeparam name="T"></typeparam> 
    /// <param name="toSerialize">To serialize.</param> 
    /// <returns></returns> 
    public static string SerializeObjectToXml<T>(this T toSerialize) 
    { 
     XmlSerializer xmlSerializer = new XmlSerializer(toSerialize.GetType()); 
     StringWriter textWriter = new StringWriter(); 

     xmlSerializer.Serialize(textWriter, toSerialize); 
     return textWriter.ToString(); 
    } 

    /// <summary> 
    /// Serializes the object. 
    /// </summary> 
    /// <typeparam name="T"></typeparam> 
    /// <param name="toSerialize">To serialize.</param> 
    /// <param name="path">The path.</param> 
    public static void SerializeObjectToFile<T>(this T toSerialize, string path) 
    { 
     string xml = SerializeObjectToXml<T>(toSerialize); 

     using (StreamWriter sw = new StreamWriter(path, false)) 
     { 
      sw.Write(xml); 
     } 
    } 

    /// <summary> 
    /// Deserializes the specified XML. 
    /// </summary> 
    /// <typeparam name="T"></typeparam> 
    /// <param name="xml">The XML.</param> 
    /// <returns></returns> 
    public static T DeserializeFromXml<T>(this T original, string xml) 
    { 
     XmlSerializer serializer = new XmlSerializer(typeof(T)); 
     TextReader textReader = new StringReader(xml); 
     return (T)serializer.Deserialize(textReader); 
    } 

    /// <summary> 
    /// Deserializes the specified object. 
    /// </summary> 
    /// <typeparam name="T"></typeparam> 
    /// <param name="original">The original.</param> 
    /// <param name="path">The path.</param> 
    /// <returns></returns> 
    public static T DeserializeFromFile<T>(this T original, string path) 
    { 
     string xml = string.Empty; 

     using (StreamReader sr = new StreamReader(path)) 
     { 
      xml = sr.ReadToEnd(); 
     } 

     return DeserializeFromXml<T>(original, xml); 
    } 
} 

Utilisation sérialiser:

YourClassType obj = new YourClassType(); 

ou

List<YourClassType> obj = new List<YourClassType>(); 

string xml = obj.SerializeObjectToXml(); 

ou

obj.SerializeObjectToFile("PathToYourFile"); // It will save a file with your classes serialized (works with everything with the [Serializable] attribute). 

Utilisation désérialiser:

YourClassType obj = new YourClassType().DeserializeFromXml("XML string here"); 
List<YourClassType> obj = new List<YourClassType>().DeserializeFromFile("XML string here"); 

ou

YourClassType obj = new YourClassType().DeserializeFromFile("PathToYourFile"); 

Et vous l'avez en cours d'exécution :)

Je préfère les méthodes d'extension, car cela vous permet d'avoir votre code très propre, cela fonctionne avec tous les types d'objets que vous avez, dans la mesure où il implémente l'attribut [Serializable].

Si vous devez spécifier comment il sera publié en feuilleton (comme des nœuds ou des attributs), vous pouvez ajouter l'attribut sur chacun de vos propriétés telles que:

[XmlElement("NameOfTheElementYouWant")] 
[XmlAttribute("NameOfTheAttributeYouWant")] 
[XmlText] 

L'espoir que cela aide quelqu'un à l'avenir.

Alejandro

Questions connexes