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?



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

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

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

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


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.


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.


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

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

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


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.


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" />

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

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

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

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

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;
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.