Serializzare un oggetto in XML


292

Ho una classe C # che ho ereditato. Ho "costruito" con successo l'oggetto. Ma devo serializzare l'oggetto in XML. C'è un modo semplice per farlo?

Sembra che la classe sia stata impostata per la serializzazione, ma non sono sicuro di come ottenere la rappresentazione XML. La definizione della mia classe è simile alla seguente:

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

Ecco cosa pensavo di poter fare, ma non funziona:

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

Come ottengo la rappresentazione XML di questo oggetto?



1
Ho sviluppato una semplice libreria per raggiungere questo obiettivo: github.com/aishwaryashiva/SaveXML
Aishwarya Shiva,

Risposte:


510

Devi usare XmlSerializer per la serializzazione XML. Di seguito è riportato un frammento di esempio.

 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
Sembra funzionare perfettamente senza la lineaXmlWriter writer = XmlWriter.Create(sww);
Paul Hunt,

15
Per formattare l'oggetto serializzato, procedi nel seguente modo: XmlTextWriter writer = new XmlTextWriter(sww) { Formatting = Formatting.Indented };anzichéXmlWriter writer = XmlWriter.Create(sww);
Tono Nam

4
Dal momento che XmlWriterincapsula il StringWriternon è necessario disporre di entrambi (il primo utilizzo è ridondante), giusto? Sto assumendo XmlWritersi prende cura di smaltire ...
Talles

4
@talles XmlWriternon sta incapsulando il StringWriter, sta utilizzando il tuo pass -in StringWritere non ha aspettative / responsabilità per smaltirlo. Inoltre StringWriterè al XmlWriterdi fuori dello scopo, potresti volerlo ancora quando XmlWriterviene smaltito, sarebbe un comportamento scadente per XmlWritersmaltire il tuo StringWriter. Come regola generale, se dichiari qualcosa che deve essere smaltito, sei responsabile del suo smaltimento. E implicito in quella regola, tutto ciò che non ti dichiari non dovresti disporre. Quindi entrambi usingsono necessari.
Arkaine55,

3
utilizzando System.Xml.Serialization; utilizzando System.IO; utilizzando System.Xml;
timothy

122

Ho modificato il mio per restituire una stringa anziché utilizzare una variabile ref come di seguito.

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

Il suo utilizzo sarebbe così:

var xmlString = obj.Serialize();

8
soluzione molto bella, mi piace il modo in cui lo hai implementato come metodo di estensione
Spyros il

57
Una cosa che suggerirei qui: rimuovere il blocco try ... catch. Non ti dà alcun vantaggio e offusca semplicemente l'errore che viene generato.
Jammycakes,

7
Non hai bisogno anche di usare il stringwriter? ad esempio: using (var stringWriter = new StringWriter ())
Steven Quick

3
@jammycakes No! Quando ne lanci uno nuovo Exception, hai esteso StackTrace con il metodo "Serialize <>".
user11909

1
@ user2190035 sicuramente se dovesse rompersi nel metodo di estensione la traccia dello stack inizierebbe da lì? "L'estensione della traccia dello stack" con il tentativo sembra inutile?
LeRoi,

43

La seguente funzione può essere copiata su qualsiasi oggetto per aggiungere una funzione di salvataggio XML utilizzando lo spazio dei nomi 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();
    }
}

Per creare l'oggetto dal file salvato, aggiungere la seguente funzione e sostituire [Tipo oggetto] con il tipo di oggetto da creare.

/// <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()è ridondante in un usingblocco - writeril Dispose()metodo lo scaricherà per te.
Baviera,

6
La mia esperienza ha scoperto che non è vero. Con dati più grandi, l'istruzione using eliminerà il flusso prima che il buffer venga cancellato. Consiglio al 100% di chiamare esplicitamente flush.
Ben Gripka,

6
writer.Flush () NON è ridondante, DEVE essere presente. Senza Flush, può capitare che parte dei dati sia ancora nel buffer StreamWriter e che il file venga eliminato e che manchino alcuni dati.
Tomas Kubes,

Mi piace molto il tuo codice: breve e pulito. Il mio problema è con la copia ripetuta delle funzioni in classi diverse: non è la duplicazione del codice? Le altre risposte suggeriscono una libreria generica con metodi di estensione del modello, che vorrei abbracciare. Cosa ne pensi?
Michael G,

33

Classe di estensione:

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 fare riferimento allo spazio dei nomi trattenere il metodo di estensione nel file che si desidera utilizzare e che funzionerà (nel mio esempio sarebbe: using MyProj.Extensions;)

Si noti che se si desidera rendere il metodo di estensione specifico solo per una determinata classe (ad es., Foo), È possibile sostituire l' Targomento nel metodo di estensione, ad es.

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


31

È possibile utilizzare la funzione come di seguito per ottenere XML serializzati da qualsiasi oggetto.

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

Puoi chiamarlo dal client.


21

Per serializzare un oggetto, eseguire:

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

Ricorda inoltre che per far funzionare XmlSerializer è necessario un costruttore senza parametri.


2
Questo mi stava facendo impazzire. Non riuscivo a capire perché fosse sempre vuoto. Poi ho capito che non avevo un costruttore senza parametri dopo aver letto la tua risposta. Grazie.
Andy,

19

Inizierò con la copia della risposta di 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();
    }
}

Ho usato questo codice prima. Ma la realtà ha dimostrato che questa soluzione è un po 'problematica. Di solito la maggior parte dei programmatori semplicemente serializza le impostazioni sul salvataggio e deserializza le impostazioni sul caricamento. Questo è uno scenario ottimistico. Una volta che la serializzazione non è riuscita, per qualche motivo, il file è stato parzialmente scritto, il file XML non è completo e non è valido. Di conseguenza, la deserializzazione XML non funziona e l'applicazione potrebbe bloccarsi all'avvio. Se il file non è enorme, suggerisco prima di serializzare l'oggetto per MemoryStreamscrivere il flusso nel file. Questo caso è particolarmente importante in caso di complicata serializzazione personalizzata. Non puoi mai testare tutti i casi.

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 deserializzazione nello scenario del mondo reale dovrebbe contare con un file di serializzazione corrotto, succede qualche volta. La funzione di caricamento fornita da Ben Gripka va bene.

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 potrebbe essere avvolto da alcuni scenari di recupero. È adatto per file di impostazioni o altri file che possono essere eliminati in caso di problemi.

public static [ObjectType] LoadWithRecovery(string fileName)
{
    try
    {
        return Load(fileName);
    }
    catch(Excetion)
    {
        File.Delete(fileName); //delete corrupted settings file
        return GetFactorySettings();
    }
}

Non è possibile interrompere il processo durante la scrittura di MemoryStream in un file, ad esempio mediante un arresto di corrente?
John Smith,

1
Sì, è possibile. È possibile evitarlo scrivendo le impostazioni in un file temporaneo e quindi sostituire l'originale.
Tomas Kubes,

18

Tutte le risposte votate sopra sono corrette. Questa è solo la versione più semplice:

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

9

È un po 'più complicato che chiamare il ToStringmetodo della classe, ma non molto.

Ecco una semplice funzione drop-in che puoi usare per serializzare qualsiasi tipo di oggetto. Restituisce una stringa contenente i contenuti XML serializzati:

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

È possibile creare e archiviare il risultato come file xml nella posizione desiderata.


4

il mio codice di lavoro. Restituisce utf8 xml abilita lo spazio dei nomi vuoto.

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

Esempio restituisce risposta 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

Ho un modo semplice per serializzare un oggetto in XML usando C #, funziona benissimo ed è altamente riutilizzabile. So che questo è un thread più vecchio, ma volevo pubblicarlo perché qualcuno potrebbe trovarlo utile.

Ecco come chiamo il metodo:

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

Ecco la classe che fa il lavoro:

Nota: poiché si tratta di metodi di estensione, devono essere in una classe statica.

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

Sulla base delle soluzioni precedenti, ecco una classe di estensione che è possibile utilizzare per serializzare e deserializzare qualsiasi oggetto. Qualsiasi altra attribuzione XML dipende da te.

Usalo in questo modo:

        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

Oppure puoi aggiungere questo metodo al tuo oggetto:

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

1

Ecco un codice di base che ti aiuterà a serializzare gli oggetti C # in 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
Sarebbe bello se citi la fonte di questo codice: 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)

È necessario utilizzare le seguenti classi:

using System.IO;
using System.Xml;
using System.Xml.Serialization;
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.