Serializar un objeto a XML

292

Tengo una clase C # que heredé. He "construido" con éxito el objeto. Pero necesito serializar el objeto a XML. ¿Hay una manera fácil de hacerlo?

Parece que la clase se ha configurado para la serialización, pero no estoy seguro de cómo obtener la representación XML. Mi definición de clase se ve así:

[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.1")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "http://www.domain.com/test")]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "http://www.domain.com/test", IsNullable = false)]
public partial class MyObject
{
  ...
}

Esto es lo que pensé que podía hacer, pero no funciona:

MyObject o = new MyObject();
// Set o properties
string xml = o.ToString();

¿Cómo obtengo la representación XML de este objeto?

usuario462166
fuente
Consulte también: Serialización y deserialización XML en CodeProject
Behzad Ebrahimi
1
Desarrollé una biblioteca simple para lograr esto: github.com/aishwaryashiva/SaveXML
Aishwarya Shiva

Respuestas:

510

Debe usar XmlSerializer para la serialización XML. A continuación se muestra un fragmento de muestra.

 XmlSerializer xsSubmit = new XmlSerializer(typeof(MyObject));
 var subReq = new MyObject();
 var xml = "";

 using(var sww = new StringWriter())
 {
     using(XmlWriter writer = XmlWriter.Create(sww))
     {
         xsSubmit.Serialize(writer, subReq);
         xml = sww.ToString(); // Your XML
     }
 }
RameshVel
fuente
10
Parece funcionar perfectamente bien sin la líneaXmlWriter writer = XmlWriter.Create(sww);
Paul Hunt
15
Para tener el objeto serializado formateado, haga: en XmlTextWriter writer = new XmlTextWriter(sww) { Formatting = Formatting.Indented };lugar deXmlWriter writer = XmlWriter.Create(sww);
Tono Nam
44
Ya que XmlWriterencapsula el StringWriterno necesita deshacerse de ambos (el primer uso es redundante), ¿verdad? Estoy asumiendo que XmlWriterse encarga de deshacerse de él ...
Talles
44
@talles XmlWriterno está encapsulando el StringWriter, está utilizando su transferencia StringWritery no tiene ninguna expectativa / responsabilidad de deshacerse de él. Además StringWriterestá fuera XmlWriterdel alcance, aún puede desearlo cuando lo deseche XmlWriter, sería un mal comportamiento XmlWriterdeshacerse de él StringWriter. Como regla general, si declaras algo que necesita ser eliminado, eres responsable de deshacerte de él. E implícito en esa regla, cualquier cosa que no declares no debes deshacerte. Entonces ambos usings son necesarios.
Arkaine55
3
usando System.Xml.Serialization; usando System.IO; usando System.Xml;
Timothy
122

Modifiqué el mío para devolver una cadena en lugar de usar una variable de referencia como a continuación.

public static string Serialize<T>(this T value)
{
    if (value == null)
    {
        return string.Empty;
    }
    try
    {
        var xmlserializer = new XmlSerializer(typeof(T));
        var stringWriter = new StringWriter();
        using (var writer = XmlWriter.Create(stringWriter))
        {
            xmlserializer.Serialize(writer, value);
            return stringWriter.ToString();
        }
    }
    catch (Exception ex)
    {
        throw new Exception("An error occurred", ex);
    }
}

Su uso sería así:

var xmlString = obj.Serialize();
Kwex
fuente
8
muy buena solución, me gusta la forma en que implementaste esto como método de extensión
Spyros
57
Una cosa que sugeriría aquí: eliminar el bloque try ... catch. No le da ningún beneficio y solo ofusca el error que se está lanzando.
jammycakes
77
¿No necesitas usar también en la máquina de escribir? por ejemplo: using (var stringWriter = new StringWriter ())
Steven Quick
3
@jammycakes ¡No! Cuando lanzas un nuevo Exceptionallí, has extendido el StackTrace con el método "Serialize <>".
user11909
1
@ user2190035 seguramente si se rompiera dentro del método de extensión, ¿el seguimiento de la pila comenzaría allí? ¿"Extender el seguimiento de la pila" con el intento parece innecesario?
LeRoi
43

La siguiente función se puede copiar a cualquier objeto para agregar una función de guardar XML utilizando el espacio de nombres System.Xml.

/// <summary>
/// Saves to an xml file
/// </summary>
/// <param name="FileName">File path of the new xml file</param>
public void Save(string FileName)
{
    using (var writer = new System.IO.StreamWriter(FileName))
    {
        var serializer = new XmlSerializer(this.GetType());
        serializer.Serialize(writer, this);
        writer.Flush();
    }
}

Para crear el objeto a partir del archivo guardado, agregue la siguiente función y reemplace [ObjectType] con el tipo de objeto a crear.

/// <summary>
/// Load an object from an xml file
/// </summary>
/// <param name="FileName">Xml file name</param>
/// <returns>The object created from the xml file</returns>
public static [ObjectType] Load(string FileName)
{
    using (var stream = System.IO.File.OpenRead(FileName))
    {
        var serializer = new XmlSerializer(typeof([ObjectType]));
        return serializer.Deserialize(stream) as [ObjectType];
    }
}
Ben Gripka
fuente
writer.Flush()es redundante en un usingbloque - writerel Dispose()método lo vaciará por usted.
bavaza
66
Mi experiencia ha encontrado que eso no es cierto. Con datos más grandes, la instrucción using eliminará la secuencia antes de que se borre el búfer. Recomiendo al 100% llamar explícitamente a flush.
Ben Gripka
66
writer.Flush () NO es redundante, DEBE estar allí. Sin Flush, puede suceder que parte de los datos todavía esté en el búfer StreamWriter y el archivo se elimine y falten algunos datos.
Tomas Kubes
Me gusta mucho tu código: corto y ordenado. Mi problema es copiar las funciones una y otra vez a diferentes clases: ¿no es duplicación de código? Las otras respuestas sugieren una biblioteca genérica con métodos de extensión de plantilla, que abrazaría. ¿Qué piensas?
Michael G
33

Clase de extensión:

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

namespace MyProj.Extensions
{
    public static class XmlExtension
    {
        public static string Serialize<T>(this T value)
        {
            if (value == null) return string.Empty;

            var xmlSerializer = new XmlSerializer(typeof(T));

            using (var stringWriter = new StringWriter())
            {
                using (var xmlWriter = XmlWriter.Create(stringWriter,new XmlWriterSettings{Indent = true}))
                {
                    xmlSerializer.Serialize(xmlWriter, value);
                    return stringWriter.ToString();
                }    
            }
        }
    }
}

Uso:

Foo foo = new Foo{MyProperty="I have been serialized"};

string xml = foo.Serialize();

Simplemente hacer referencia al espacio de nombres de la celebración de su método de extensión en el archivo que desea utilizar y en que va a trabajar (en mi ejemplo sería: using MyProj.Extensions;)

Tenga en cuenta que si desea hacer que el método de extensión sea específico solo para una clase en particular (por ejemplo, Foo), puede reemplazar el Targumento en el método de extensión, por ejemplo.

public static string Serialize(this Foo value){...}

Aleksandr Albert
fuente
31

Puede usar la función que se muestra a continuación para obtener XML serializado de cualquier objeto.

public static bool Serialize<T>(T value, ref string serializeXml)
{
    if (value == null)
    {
        return false;
    }
    try
    {
        XmlSerializer xmlserializer = new XmlSerializer(typeof(T));
        StringWriter stringWriter = new StringWriter();
        XmlWriter writer = XmlWriter.Create(stringWriter);

        xmlserializer.Serialize(writer, value);

        serializeXml = stringWriter.ToString();

        writer.Close();
        return true;
    }
    catch (Exception ex)
    {
        return false;
    }
}

Puedes llamar a esto desde el cliente.

Imrul
fuente
21

Para serializar un objeto, haga:

 using (StreamWriter myWriter = new StreamWriter(path, false))
 {
     XmlSerializer mySerializer = new XmlSerializer(typeof(your_object_type));
     mySerializer.Serialize(myWriter, objectToSerialize);
 }

También recuerde que para que XmlSerializer funcione, necesita un constructor sin parámetros.

Rox
fuente
2
Esto me estaba volviendo loco. No podía entender por qué siempre estaba en blanco. Luego me di cuenta de que no tenía un constructor sin parámetros después de leer su respuesta. Gracias.
Andy
19

Comenzaré con la copia de la respuesta de Ben Gripka:

public void Save(string FileName)
{
    using (var writer = new System.IO.StreamWriter(FileName))
    {
        var serializer = new XmlSerializer(this.GetType());
        serializer.Serialize(writer, this);
        writer.Flush();
    }
}

Usé este código antes. Pero la realidad mostró que esta solución es un poco problemática. Por lo general, la mayoría de los programadores solo serializan la configuración al guardar y deserializan la configuración al cargar. Este es un escenario optimista. Una vez que la serialización falló, por alguna razón, el archivo está parcialmente escrito, el archivo XML no está completo y no es válido. En consecuencia, la deserialización XML no funciona y su aplicación puede bloquearse al inicio. Si el archivo no es enorme, sugiero primero serializar el objeto para MemoryStreamluego escribir la secuencia en el Archivo. Este caso es especialmente importante si hay alguna serialización personalizada complicada. Nunca puedes probar todos los casos.

public void Save(string fileName)
{
    //first serialize the object to memory stream,
    //in case of exception, the original file is not corrupted
    using (MemoryStream ms = new MemoryStream())
    {
        var writer = new System.IO.StreamWriter(ms);    
        var serializer = new XmlSerializer(this.GetType());
        serializer.Serialize(writer, this);
        writer.Flush();

        //if the serialization succeed, rewrite the file.
        File.WriteAllBytes(fileName, ms.ToArray());
    }
}

La deserialización en el escenario del mundo real debe contar con un archivo de serialización dañado, sucede en algún momento. La función de carga proporcionada por Ben Gripka está bien.

public static [ObjectType] Load(string fileName)
{
    using (var stream = System.IO.File.OpenRead(fileName))
    {
        var serializer = new XmlSerializer(typeof([ObjectType]));
        return serializer.Deserialize(stream) as [ObjectType];        
    }    
}

Y podría estar envuelto por algún escenario de recuperación. Es adecuado para archivos de configuración u otros archivos que se pueden eliminar en caso de problemas.

public static [ObjectType] LoadWithRecovery(string fileName)
{
    try
    {
        return Load(fileName);
    }
    catch(Excetion)
    {
        File.Delete(fileName); //delete corrupted settings file
        return GetFactorySettings();
    }
}
Tomás Kubes
fuente
¿No es posible que el proceso se interrumpa mientras se escribe MemoryStream en un archivo, por ejemplo, por un corte de energía?
John Smith
1
Sí, es posible. Puede evitarlo escribiendo la configuración en un archivo temporal y luego reemplace el original.
Tomas Kubes
18

Todas las respuestas votadas arriba son correctas. Esta es la versión más simple:

private string Serialize(Object o)
{
    using (var writer = new StringWriter())
    {
        new XmlSerializer(o.GetType()).Serialize(writer, o);
        return writer.ToString();
    }
}
avj
fuente
9

Es un poco más complicado que llamar al ToStringmétodo de la clase, pero no mucho.

Aquí hay una función simple que puede usar para serializar cualquier tipo de objeto. Devuelve una cadena que contiene los contenidos XML serializados:

public string SerializeObject(object obj)
{
    System.Xml.XmlDocument xmlDoc = new System.Xml.XmlDocument();
    System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(obj.GetType());
    using (System.IO.MemoryStream ms = new System.IO.MemoryStream()) {
        serializer.Serialize(ms, obj);
        ms.Position = 0;
        xmlDoc.Load(ms);
        return xmlDoc.InnerXml;
    }
}
Cody Gray
fuente
4
    string FilePath = ConfigurationReader.FileLocation;   //Getting path value from web.config            
    XmlSerializer serializer = new XmlSerializer(typeof(Devices)); //typeof(object)
            MemoryStream memStream = new MemoryStream();
            serializer.Serialize(memStream, lstDevices);//lstdevices : I take result as a list.
            FileStream file = new FileStream(folderName + "\\Data.xml", FileMode.Create, FileAccess.ReadWrite); //foldername:Specify the path to store the xml file
            memStream.WriteTo(file);
            file.Close();

Puede crear y almacenar el resultado como archivo xml en la ubicación deseada.

Dev Try
fuente
4

Mi código de trabajo. Devuelve utf8 xml habilitar espacio de nombres vacío.

// override StringWriter
public class Utf8StringWriter : StringWriter
{
    public override Encoding Encoding => Encoding.UTF8;
}

private string GenerateXmlResponse(Object obj)
{    
    Type t = obj.GetType();

    var xml = "";

    using (StringWriter sww = new Utf8StringWriter())
    {
        using (XmlWriter writer = XmlWriter.Create(sww))
        {
            var ns = new XmlSerializerNamespaces();
            // add empty namespace
            ns.Add("", "");
            XmlSerializer xsSubmit = new XmlSerializer(t);
            xsSubmit.Serialize(writer, obj, ns);
            xml = sww.ToString(); // Your XML
        }
    }
    return xml;
}

Ejemplo devuelve respuesta Yandex api pago Aviso url:

<?xml version="1.0" encoding="utf-8"?><paymentAvisoResponse xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" performedDatetime="2017-09-01T16:22:08.9747654+07:00" code="0" shopId="54321" invoiceId="12345" orderSumAmount="10643" />
dev-siberia
fuente
4

Tengo una manera simple de serializar un objeto a XML usando C #, funciona muy bien y es altamente reutilizable. Sé que este es un hilo antiguo, pero quería publicar esto porque alguien puede encontrar esto útil para ellos.

Así es como llamo al método:

var objectToSerialize = new MyObject();
var xmlString = objectToSerialize.ToXmlString();

Aquí está la clase que hace el trabajo:

Nota: Dado que estos son métodos de extensión, deben estar en una clase estática.

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

public static class XmlTools
{
    public static string ToXmlString<T>(this T input)
    {
        using (var writer = new StringWriter())
        {
            input.ToXml(writer);
            return writer.ToString();
        }
    }

    private static void ToXml<T>(this T objectToSerialize, StringWriter writer)
    {
        new XmlSerializer(typeof(T)).Serialize(writer, objectToSerialize);
    }
}
Tyler Kalosza
fuente
4

Basado en las soluciones anteriores, aquí viene una clase de extensión que puede usar para serializar y deserializar cualquier objeto. Cualquier otra atribución XML depende de usted.

Solo úsalo así:

        string s = new MyObject().Serialize(); // to serialize into a string
        MyObject b = s.Deserialize<MyObject>();// deserialize from a string



internal static class Extensions
{
    public static T Deserialize<T>(this string value)
    {
        var xmlSerializer = new XmlSerializer(typeof(T));

        return (T)xmlSerializer.Deserialize(new StringReader(value));
    }

    public static string Serialize<T>(this T value)
    {
        if (value == null)
            return string.Empty;

        var xmlSerializer = new XmlSerializer(typeof(T));

        using (var stringWriter = new StringWriter())
        {
            using (var xmlWriter = XmlWriter.Create(stringWriter, new XmlWriterSettings { Indent = true }))
            {
                xmlSerializer.Serialize(xmlWriter, value);
                return stringWriter.ToString();
            }
        }
    }
}
Hefaistos68
fuente
2

O puede agregar este método a su objeto:

    public void Save(string filename)
    {
        var ser = new XmlSerializer(this.GetType());
        using (var stream = new FileStream(filename, FileMode.Create))
            ser.Serialize(stream, this);
    }
Bigjim
fuente
1

Aquí hay un código básico que ayudará a serializar los objetos de C # en xml:

using System;

public class clsPerson
{
  public  string FirstName;
  public  string MI;
  public  string LastName;
}

class class1
{ 
   static void Main(string[] args)
   {
      clsPerson p=new clsPerson();
      p.FirstName = "Jeff";
      p.MI = "A";
      p.LastName = "Price";
      System.Xml.Serialization.XmlSerializer x = new System.Xml.Serialization.XmlSerializer(p.GetType());
      x.Serialize(Console.Out, p);
      Console.WriteLine();
      Console.ReadLine();
   }
}    
Ali Asad
fuente
66
Sería bueno si
citaras la
0
public string ObjectToXML(object input)
{
    try
    {
        var stringwriter = new System.IO.StringWriter();
        var serializer = new XmlSerializer(input.GetType());
        serializer.Serialize(stringwriter, input);
        return stringwriter.ToString();
    }
    catch (Exception ex)
    {
        if (ex.InnerException != null)
            ex = ex.InnerException;

        return "Could not convert: " + ex.Message;
    }
}

//Usage
var res = ObjectToXML(obj)

Necesita usar las siguientes clases:

using System.IO;
using System.Xml;
using System.Xml.Serialization;
Sagar Timalsina
fuente