2009-07-04 13 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(); 

     } 
    } 
} 

Logré Guardar sin problema, todos los miembros de FolderOption están deserializados. Pero el problema es cómo leerlo de nuevo? La línea - // this = (Opciones) deserializer.Deserialize (textReader); no funcionaráC# - ¿Cómo deshabilitar el objeto xml?

Editar: ¿Alguna solución a este problema? ¿Podemos lograr el mismo propósito sin asignar esto? Eso es deserializar el objeto Options nuevamente en Option. Soy flojo para hacerlo propiedad por propiedad. Realizar en el nivel más alto ahorraría mucho esfuerzo.

Respuesta

11

Esto funcionará si su tipo de Opciones es una estructura, ya que puede modificar una estructura.

Si Options es una clase (tipo de referencia), no se puede asignar a la instancia actual de un tipo de referencia en esa instancia. Lo que sugiere que para escribir una clase de ayuda, y puso sus métodos de leer y guardar allí, como esto

 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; 

     } 
    } 

Y luego lo consumen de su interlocutor, para leer y guardar objetos, en lugar de tratar desde la clase.

//In the caller 

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

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

y poner el XmlSerializerHelper en el espacio de nombres de Util, es reutilizable y funciona con cualquier tipo.

+0

-1 por no implementar bloques "usar" y por no usar genéricos. –

+0

usando (XmlSerializer deserializer = new XmlSerializer (_type)) no funciona. XmlSerializer no implementó IDisposable. El correcto debería ser como la respuesta de John, poniendo XmlSerializer fuera del bloque de uso. –

+0

Vaya, olvidé por un momento que XmlSerializer no implementó IDisposable, corregido :) – amazedsaint

0

Ver XmlSerializer.Deserialize Method: Se puede crear un método estático como el siguiente:

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 
    } 

Lo anterior puede ser llamado como:

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

1) Por favor, lea su pregunta con más cuidado. 2) -1 por no usar bloques "usando". –

+0

Tienes razón, debería. –

+0

Aún necesita usar alrededor del XmlReader. –

5

un objeto no puede deserializar en sí, por definición: ya existe y la deserialización crea una nueva instancia del tipo.

A veces tiene sentido crear una nueva instancia vacía de una clase, y luego completarla con información proveniente de XML. La instancia también podría estar "casi vacía". Puede hacer esto, por ejemplo, para cargar las preferencias del usuario o, en general, para volver a configurar la instancia como solía ser. El estado "vacío" o "casi vacío" de la instancia sería un estado válido para la clase: simplemente no sabría en qué estado estaba antes de que persistiera.


Además, le recomiendo que entra en el hábito de poner en práctica "utilizando" bloques:

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

Esto asegurará que los TextReaders se desechan incluso si se produce una excepción. Es por eso que las llamadas Cerrar ya no son necesarias.

+1

Buen recordatorio, cambiará a usar bloque. –

18

Construye tu método .Read() como una función estática que devuelve el objeto de lectura:

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

a continuación, cambiar su código de llamada por lo que en lugar de algo como esto:

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

haces algo como esto:

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

La buena diferencia es que es imposible nunca tendrá un objeto Options que no tenga datos detrás.

+0

Acepto, esta IMO es la solución más limpia que responde la pregunta. –

+0

No puedo comentar sobre los demás, pero esto se ajusta mejor a mi proyecto actual ... muy claro. – CJM

1

yo fuimos a este enfoque (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 

puedo simplemente usar algo como esto:

Public Class ClientSettings 

    Inherits SerialisableClass 

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

End Class 

y lo llaman así:

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

o incluso mejor (si agrego el constructor necesario):

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

Me parece agradable y limpio y funciona bien en mi situación.

Saludos

2

Creo que la forma más simple para serializar y deserializar un objeto es utilizar una clase estática con los dos métodos siguientes. También necesitamos una clase llamada StringWriterWithEncoding para establecer la codificación de la cadena XML, ya que la propiedad Codificación de la clase StringWriter estándar es de solo lectura.(Que se encuentra aquí: 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; } 
    } 
} 

Uso:

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

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

Soy fan de los métodos de extensión, por lo tanto, yo uso esto siempre:

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

uso para serializar:

YourClassType obj = new YourClassType(); 

o

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

string xml = obj.SerializeObjectToXml(); 

o

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

Uso deserializar:

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

o

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

y lo tienes funcionando :)

Prefiero los métodos de extensión porque le permite tener su código muy limpio, esto funciona con todo tipo de tipo de objeto que tenga, en la medida en que implemente el atributo [Serializable].

Si es necesario especificar cómo se va a serializar (como nodos o atributos), se puede añadir el atributo en cada una de sus propiedades, tales como:

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

Espero que esto ayude a alguien en el futuro.

Alejandro