Serializar um objeto para XML

292

Eu tenho uma classe c # que eu herdei. Eu "construí" com sucesso o objeto. Mas preciso serializar o objeto para XML. Existe uma maneira fácil de fazer isso?

Parece que a classe foi configurada para serialização, mas não sei como obter a representação XML. Minha definição de classe é assim:

[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
{
  ...
}

Aqui está o que eu pensei que poderia fazer, mas não funciona:

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

Como obtenho a representação XML deste objeto?

user462166
fonte
1
Eu desenvolvi uma biblioteca simples para conseguir isso: github.com/aishwaryashiva/SaveXML
Aishwarya Shiva

Respostas:

510

Você precisa usar o XmlSerializer para serialização XML. Abaixo está um exemplo de trecho.

 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
fonte
10
Parece funcionar perfeitamente sem a linhaXmlWriter writer = XmlWriter.Create(sww);
Paul Hunt
15
Para ter objeto serializado formatado fazer: XmlTextWriter writer = new XmlTextWriter(sww) { Formatting = Formatting.Indented };em vez deXmlWriter writer = XmlWriter.Create(sww);
Tono Nam
4
Como XmlWriterencapsula o que StringWritervocê não precisa dispor dos dois (o primeiro uso é redundante), certo? Estou assumindo que XmlWriterse encarrega de eliminá-lo ...
Talles
4
O @talles XmlWriternão está encapsulando StringWriter, está utilizando o seu passe StringWritere não tem nenhuma expectativa / responsabilidade de descartá-lo. Além disso StringWriterestá fora XmlWriterdo escopo, você ainda pode querer quando XmlWriterdescartado, seria um mau comportamento para XmlWriterdescartar o seu StringWriter. Como regra geral, se você declarar algo que precisa ser descartado, é responsável por descartá-lo. E implícito a essa regra, qualquer coisa que você não se declara não deve descartar. Então, ambos usingsão necessários.
Arkaine55
3
using System.Xml.Serialization; using System.IO; using System.Xml;
Timothy
122

Modifiquei o meu para retornar uma string em vez de usar uma variável ref como abaixo.

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

Seu uso seria assim:

var xmlString = obj.Serialize();
Kwex
fonte
8
muito agradável solução, eu gosto da maneira como você implementou este como um método de extensão
Spyros
57
Uma coisa que eu sugiro aqui: remover o bloco try ... catch. Não oferece nenhum benefício e apenas ofusca o erro que está sendo lançado.
jammycakes
7
Você também não precisa usar o stringwriter? por exemplo: using (var stringWriter = new StringWriter ())
Steven Quick
3
@jammycakes Não! Quando você lança um novo Exceptionlá, você estendeu o StackTrace com o método "Serialize <>".
user11909
1
@ user2190035 certamente, se fosse para quebrar dentro do método de extensão, o rastreamento de pilha começaria por aí? "Estender o rastreamento de pilha" com a tentativa parece desnecessário?
LeRoi
43

A função a seguir pode ser copiada para qualquer objeto para adicionar uma função de salvar XML usando o espaço para nome 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 criar o objeto a partir do arquivo salvo, adicione a seguinte função e substitua [ObjectType] pelo tipo de objeto a ser criado.

/// <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
fonte
writer.Flush()é redundante em um usingbloco - writer's Dispose()método irá liberá-lo para você.
bavaza
6
Minha experiência descobriu que isso não é verdade. Com dados maiores, a instrução using descartará o fluxo antes que o buffer seja limpo. Eu 100% recomendo explicitamente chamar flush.
Ben Gripka
6
writer.Flush () NÃO é redundante, DEVE estar lá. Sem o Flush, pode acontecer que parte dos dados ainda esteja no buffer do StreamWriter e o arquivo seja descartado e alguns dados estejam ausentes.
Tomas Kubes
Gosto muito do seu código: curto e limpo. Meu problema é copiar as funções repetidamente para diferentes classes: não é duplicação de código? As outras respostas sugerem uma biblioteca genérica com métodos de extensão de modelo, que eu adotaria. O que você acha?
Michael G
33

Classe de extensão:

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

Basta referenciar o espaço para nome que contém seu método de extensão no arquivo em que você gostaria de usá-lo e ele funcionará (no meu exemplo seria: using MyProj.Extensions; )

Observe que, se você quiser tornar o método de extensão específico para apenas uma classe específica (por exemplo, Foo), poderá substituir o Targumento no método de extensão, por exemplo.

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

Aleksandr Albert
fonte
31

Você pode usar a função abaixo para obter XML serializado de qualquer 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;
    }
}

Você pode chamar isso do cliente.

Imrul
fonte
21

Para serializar um objeto, faça:

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

Lembre-se também de que, para o XmlSerializer funcionar, você precisa de um construtor sem parâmetros.

Rox
fonte
2
Isso estava me deixando louco. Eu não conseguia descobrir por que estava sempre em branco. Então percebi que não tinha um construtor sem parâmetros depois de ler sua resposta. Obrigado.
Andy Andy
19

Começarei com a resposta de cópia 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();
    }
}

Eu usei esse código anteriormente. Mas a realidade mostrou que esta solução é um pouco problemática. Normalmente, a maioria dos programadores apenas serializa as configurações em salvar e desserializa as configurações em carga. Este é um cenário otimista. Depois que a serialização falhou, por algum motivo, o arquivo é parcialmente gravado, o arquivo XML não está completo e é inválido. Consequentemente, a desserialização de XML não funciona e seu aplicativo pode falhar no início. Se o arquivo não for grande, sugiro primeiro serializar o objeto para MemoryStreamgravar o fluxo no arquivo. Este caso é especialmente importante se houver alguma serialização personalizada complicada. Você nunca pode testar todos os 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());
    }
}

A desserialização no cenário do mundo real deve contar com o arquivo de serialização corrompido, isso acontece em algum momento. A função de carga fornecida por Ben Gripka está correta.

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

E pode ser envolvido por algum cenário de recuperação. É adequado para arquivos de configurações ou outros arquivos que podem ser excluídos em caso de problemas.

public static [ObjectType] LoadWithRecovery(string fileName)
{
    try
    {
        return Load(fileName);
    }
    catch(Excetion)
    {
        File.Delete(fileName); //delete corrupted settings file
        return GetFactorySettings();
    }
}
Tomas Kubes
fonte
Não é possível interromper o processo durante a gravação do MemoryStream em um arquivo, por exemplo, por um desligamento da energia?
John Smith
1
Sim, é possível. Você pode evitá-lo gravando as configurações em um arquivo temporário e substituindo o original.
Tomas Kubes
18

Todas as respostas acima votadas estão corretas. Esta é apenas a versão mais simples:

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

É um pouco mais complicado do que chamar o ToStringmétodo da classe, mas não muito.

Aqui está uma função simples que você pode usar para serializar qualquer tipo de objeto. Retorna uma string contendo o conteúdo XML serializado:

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

Você pode criar e armazenar o resultado como arquivo xml no local desejado.

Dev Try
fonte
4

meu código de trabalho. Retorna utf8 xml enable espaço para nome vazio.

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

Exemplo retorna resposta Yandex api payment 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
fonte
4

Eu tenho uma maneira simples de serializar um objeto para XML usando C #, funciona muito bem e é altamente reutilizável. Eu sei que esse é um tópico mais antigo, mas eu queria postar isso porque alguém pode achar isso útil para eles.

Aqui está como eu chamo o método:

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

Aqui está a classe que faz o trabalho:

Nota: Como esses são métodos de extensão, eles precisam estar em uma classe 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
fonte
4

Com base nas soluções acima, aqui está uma classe de extensão que você pode usar para serializar e desserializar qualquer objeto. Quaisquer outras atribuições XML dependem de você.

Apenas use-o assim:

        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
fonte
2

Ou você pode adicionar este método ao seu 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
fonte
1

Aqui está um código básico que ajudará a serializar os objetos C # em 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
fonte
6
Seria bom se você citar fonte desse código: support.microsoft.com/en-us/help/815813/...
MaLiN2223
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)

Você precisa usar as seguintes classes:

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