2009-07-04 12 views
7

Ich habe es geschafft, ohne Problem zu speichern, alle Mitglieder von FolderOption sind deserialisiert. Aber das Problem ist, wie man es zurück liest? Die Zeile - // dies = (Optionen) deserializer.Deserialize (textReader); wird nicht funktionieren.C# - Wie XML Objekt deserialize selbst?

Edit: Jede Lösung für dieses Problem? Können wir den gleichen Zweck erreichen, ohne diesem zuzuordnen? Das ist Deserialize Optionen Objekt zurück in Option. Ich bin faul, es Eigentum für Eigentum zu tun. Auf höchstem Niveau zu arbeiten würde viel Aufwand sparen.

Antwort

11

Dies funktioniert, wenn Ihr Options-Typ eine Struktur ist, da Sie eine Struktur selbst ändern können.

Wenn Optionen eine Klasse (Referenztyp) ist, können Sie der aktuellen Instanz eines Referenztyps in diesem Fall nicht zuweisen. Sie vorschlag eine Hilfsklasse zu schreiben, und setzen Sie Ihre Lesen und Speichern Methoden gibt, wie diese

 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; 

     } 
    } 

Und dann verbrauchen sie von Ihrem Anrufer, zu lesen und Objekte zu speichern, anstatt sie aus der Klasse versuchen.

//In the caller 

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

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

Und legen den XmlSerializerHelper in Ihrem Util Namespace, ist es wieder verwendbar und wird mit jeder Art arbeiten.

+0

-1 für das Nicht-Implementieren von "using" -Blöcken und das Nicht-Verwenden von Generics. –

+0

mit (XmlSerializer deserializer = neue XmlSerializer (_type)) funktioniert nicht. XmlSerializer hat IDisposable nicht implementiert. Die richtige sollte wie die Antwort von John sein und XmlSerializer außerhalb des using-Blocks setzen. –

+0

Ups, vergaß für einen Moment, dass XmlSerializer IDisposable nicht implementiert, korrigiert :) – amazedsaint

0

Siehe XmlSerializer.Deserialize Method: Sie könnten eine statische Methode wie folgt erstellen:

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 
    } 

Das Obige kann wie folgt aufgerufen werden:

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

1) Bitte lesen Sie seine Frage sorgfältiger. 2) -1 für die Nichtverwendung von "using" -Blöcken. –

+0

Sie haben Recht, ich sollte. –

+0

Sie benötigen immer noch eine Verwendung rund um den XmlReader. –

5

Ein Objekt nicht selbst per definitionem deserialisieren kann: es existiert bereits und Deserialisierung erstellt eine neue Instanz des Typs.

Manchmal ist es sinnvoll, eine neue leere Instanz einer Klasse zu erstellen und sie dann mit Informationen aus XML einzufügen. Die Instanz könnte auch "fast leer" sein. Sie können dies zum Beispiel tun, um Benutzereinstellungen zu laden oder um die Instanz im Allgemeinen wieder so einzurichten, wie sie einmal war. Der Status "leer" oder "fast leer" der Instanz wäre ein gültiger Status für die Klasse: Sie würde einfach nicht wissen, in welchem ​​Zustand sie sich befand, bevor sie beibehalten wurde.


Auch empfehle ich Ihnen in die Gewohnheit der Umsetzung bekommen „Verwendung“ Blöcke:

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

Dadurch wird sichergestellt, dass die TextReaders selbst angeordnet sind, wenn eine Ausnahme ausgelöst wird. Deshalb werden die Close-Aufrufe nicht mehr benötigt.

+1

Gute Erinnerung, ändert sich in die Verwendung von Block. –

18

Erstellen Sie Ihre .Read() Methode als eine statische Funktion, die das Lese-Objekt zurückgibt:

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

Dann Ihre Berufung Code ändern, so anstatt etwas wie folgt aus:

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

Sie etwas tun, wie folgt aus:

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

Der nette Unterschied ist, dass es unmöglich ist Möglich, ein Options-Objekt zu haben, das keine Daten enthält.

+0

Ich stimme zu, diese IMO ist die sauberste Lösung, die die Frage beantwortet. –

+0

Ich kann nicht für andere kommentieren, aber das passt mein aktuelles Projekt am besten ... sehr ordentlich. – CJM

1

ich für diesen Ansatz ging (in 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 

ich so etwas wie diese dann einfach verwenden kann:

Public Class ClientSettings 

    Inherits SerialisableClass 

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

End Class 

und es so nennen:

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

oder noch besser (wenn ich den notwendigen Konstruktor hinzufüge):

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

Es scheint nett und sauber zu mir und funktioniert gut in meiner Situation.

Prost

2

Ich denke, der einfachste Weg, ein Objekt ist eine statische Klasse mit den folgenden zwei Methoden zur Serialisierung und Deserialisierung zu verwenden. Wir benötigen außerdem eine Klasse namens StringWriterWithEncoding, um die Codierung der XML-Zeichenfolge festzulegen, da die Encoding-Eigenschaft der Standard-StringWriter-Klasse schreibgeschützt ist.(Gefunden hier: 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; } 
    } 
} 

Verbrauch:

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

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

Ich bin Fan von Erweiterungsmethoden, deshalb benutze ich diese immer:

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

Nutzungs zu serialisiert:

YourClassType obj = new YourClassType(); 

oder

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

string xml = obj.SerializeObjectToXml(); 

oder

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

Nutzungs deserialisieren:

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

oder

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

Und Sie haben es läuft :)

Ich bevorzuge Erweiterungsmethoden, weil es Ihnen erlaubt, Ihren Code sehr sauber zu haben, dies funktioniert mit jeder Art von Objekttyp, den Sie haben, sofern es das Attribut [Serializable] implementiert.

Wenn Sie angeben müssen, wie es (als Knoten oder Attribute) serialisiert werden, können Sie das Attribut auf jedem Ihrer Eigenschaften hinzufügen wie:

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

Hope this jemand in der Zukunft hilft.

Alejandro

Verwandte Themen