Serializuj obiekt na XML

292

Mam odziedziczoną klasę C #. Pomyślnie „zbudowałem” obiekt. Ale muszę serializować obiekt do formatu XML. Czy jest na to łatwy sposób?

Wygląda na to, że klasa została skonfigurowana do serializacji, ale nie jestem pewien, jak uzyskać reprezentację XML. Moja definicja klasy wygląda następująco:

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

Oto, co myślałem, że mogę zrobić, ale to nie działa:

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

Jak uzyskać reprezentację XML tego obiektu?

użytkownik462166
źródło
Zobacz także: Serializacja i deserializacja XML w CodeProject
Behzad Ebrahimi
1
W tym celu opracowałem prostą bibliotekę: github.com/aishwaryashiva/SaveXML
Aishwarya Shiva

Odpowiedzi:

510

Musisz użyć XmlSerializer do serializacji XML. Poniżej znajduje się przykładowy fragment kodu.

 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
źródło
10
Wydaje się, że działa idealnie bez liniiXmlWriter writer = XmlWriter.Create(sww);
Paul Hunt
15
Aby sformatować obiekt serializowany wykonaj: XmlTextWriter writer = new XmlTextWriter(sww) { Formatting = Formatting.Indented };zamiastXmlWriter writer = XmlWriter.Create(sww);
Tono Nam
4
Ponieważ XmlWriterenkapsuluje StringWriter, nie musisz usuwać obu (pierwsze użycie jest zbędne), prawda? Zakładam, że XmlWritersię tym pozbywam ...
wznosi
4
@talles XmlWriternie opisuje StringWriter, wykorzystuje swoje dane osobowe StringWriteri nie ma żadnych oczekiwań / odpowiedzialności za ich usunięcie. Co więcej, StringWriterjest poza XmlWriterzakresem, możesz nadal chcieć, gdy XmlWriterzostanie wyrzucony, byłoby złym zachowaniem, XmlWriteraby pozbyć się twojego StringWriter. Zasadniczo, jeśli zadeklarujesz coś, co wymaga usunięcia, jesteś odpowiedzialny za jego usunięcie. I w domniemaniu do tej zasady, niczego, czego sam nie zadeklarujesz, nie powinieneś pozbywać się. Więc oba usingsą konieczne.
Arkaine55
3
using System.Xml.Serialization; using System.IO; using System.Xml;
tymoteusz
122

Zmodyfikowałem mój, aby zwracał ciąg zamiast używać zmiennej ref, jak poniżej.

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

Jego użycie byłoby takie:

var xmlString = obj.Serialize();
Kwex
źródło
8
bardzo fajne rozwiązanie, podoba mi się sposób, w jaki zaimplementowałeś to jako metodę rozszerzenia
Spyros
57
Jedną rzecz, którą zasugeruję tutaj: usuń blok try ... catch. Nie przynosi żadnych korzyści, a jedynie zaciemnia zgłaszany błąd.
jammycakes
7
Czy nie potrzebujesz również używać w pisarzu znaków? np .: using (var stringWriter = new StringWriter ())
Steven Quick
3
@jammycakes Nie! Kiedy Exceptionrzucasz tam nowy , rozszerzyłeś StackTrace o metodę „Serialize <>”.
user11909
1
@ user2190035 na pewno, gdyby miał się złamać w metodzie rozszerzenia, ślad stosu by się tam zaczął? Czy „rozszerzenie śledzenia stosu” przy próbie wydaje się niepotrzebne?
LeRoi
43

Poniższą funkcję można skopiować do dowolnego obiektu, aby dodać funkcję zapisu XML przy użyciu przestrzeni nazw 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();
    }
}

Aby utworzyć obiekt z zapisanego pliku, dodaj następującą funkcję i zastąp [ObjectType] typem obiektu, który chcesz utworzyć.

/// <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
źródło
writer.Flush()jest zbędny w usingbloku - writerjest Dispose()metoda będzie opróżnić go dla Ciebie.
bawaza,
6
Z mojego doświadczenia wynika, że ​​to nieprawda. W przypadku większych danych instrukcja using usunie strumień przed wyczyszczeniem bufora. W 100% polecam jawne sprawdzenie koloru.
Ben Gripka
6
writer.Flush () NIE jest redundantny, MUSI tam być. Bez Flush może się zdarzyć, że część danych nadal znajduje się w buforze StreamWriter i plik zostaje usunięty, a niektórych danych brakuje.
Tomas Kubes
Bardzo podoba mi się twój kod: krótki i schludny. Mój problem polega na ciągłym kopiowaniu funkcji do różnych klas: czy to nie duplikacja kodu? Inne odpowiedzi sugerują ogólną bibliotekę z metodami rozszerzania szablonów, którą chciałbym przyjąć. Co myślisz?
Michael G,
33

Klasa rozszerzenia:

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

Stosowanie:

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

string xml = foo.Serialize();

Wystarczy odwołać się do nazw trzymając swoją metodę rozszerzenia w pliku chcesz go używać i będzie działać (w moim przykładzie będzie to: using MyProj.Extensions;)

Zauważ, że jeśli chcesz, aby metoda rozszerzenia była specyficzna tylko dla określonej klasy (np., Foo), Możesz zastąpić Targument w metodzie rozszerzenia, np.

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

Aleksandr Albert
źródło
31

Możesz użyć funkcji jak poniżej, aby uzyskać serializowany kod XML z dowolnego obiektu.

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

Możesz to nazwać od klienta.

Imrul
źródło
21

Aby serializować obiekt, wykonaj:

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

Pamiętaj również, że aby XmlSerializer działał, potrzebujesz konstruktora bez parametrów.

Rox
źródło
2
To doprowadzało mnie do szału. Nie mogłem zrozumieć, dlaczego zawsze było puste. Potem zrozumiałem, że nie mam konstruktora bez parametrów po przeczytaniu twojej odpowiedzi. Dziękuję Ci.
Andy,
19

Zacznę od kopii odpowiedzi Bena Gripki:

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

Użyłem tego kodu wcześniej. Rzeczywistość pokazała jednak, że to rozwiązanie jest nieco problematyczne. Zwykle większość programistów po prostu serializuje ustawienia zapisywania i deserializuje ustawienia przy ładowaniu. To optymistyczny scenariusz. Gdy serializacja się nie powiodła, z jakiegoś powodu plik jest częściowo zapisywany, plik XML nie jest kompletny i jest nieprawidłowy. W rezultacie deserializacja XML nie działa, a aplikacja może ulec awarii podczas uruchamiania. Jeśli plik nie jest duży, sugeruję najpierw serializować obiekt, MemoryStreama następnie zapisać strumień do pliku. Ten przypadek jest szczególnie ważny, jeśli występuje skomplikowana niestandardowa serializacja. Nigdy nie możesz przetestować wszystkich przypadków.

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

Scenariusz deserializacji w świecie rzeczywistym powinien się liczyć z uszkodzonym plikiem serializacji, zdarza się to kiedyś. Funkcja ładowania zapewniona przez Ben Gripka jest w porządku.

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

I może być zawarty w scenariuszu odzyskiwania. Nadaje się do plików ustawień lub innych plików, które można usunąć w razie problemów.

public static [ObjectType] LoadWithRecovery(string fileName)
{
    try
    {
        return Load(fileName);
    }
    catch(Excetion)
    {
        File.Delete(fileName); //delete corrupted settings file
        return GetFactorySettings();
    }
}
Tomas Kubes
źródło
Czy nie można przerwać procesu podczas zapisywania MemoryStream do pliku, na przykład przez wyłączenie zasilania?
John Smith,
1
Tak to mozliwe. Można tego uniknąć, zapisując ustawienia do pliku tymczasowego, a następnie zastępując oryginał.
Tomas Kubes
18

Wszystkie wyżej wymienione odpowiedzi są poprawne. To jest po prostu najprostsza wersja:

private string Serialize(Object o)
{
    using (var writer = new StringWriter())
    {
        new XmlSerializer(o.GetType()).Serialize(writer, o);
        return writer.ToString();
    }
}
AV J
źródło
9

To jest trochę bardziej skomplikowane niż dzwonienie na numer ToString metody klasy, ale niewiele.

Oto prosta funkcja rozwijania, której można użyć do serializacji dowolnego typu obiektu. Zwraca ciąg zawierający serializowaną zawartość XML:

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
źródło
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();

Możesz utworzyć i zapisać wynik jako plik xml w wybranej lokalizacji.

Dev Try
źródło
4

mój kod pracy. Zwraca utf8 xml włącz pustą przestrzeń nazw.

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

Przykład zwraca odpowiedź Adres URL płatności AiSo Yandex:

<?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
źródło
4

Mam prosty sposób na serializację obiektu do XML za pomocą C #, działa świetnie i jest bardzo wielokrotnego użytku. Wiem, że to starszy wątek, ale chciałem to opublikować, ponieważ ktoś może uznać to za pomocne.

Oto jak nazywam metodę:

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

Oto klasa, która działa:

Uwaga: Ponieważ są to metody rozszerzeń, muszą należeć do klasy statycznej.

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
źródło
4

W oparciu o powyższe rozwiązania nadchodzi klasa rozszerzenia, której można użyć do serializacji i deserializacji dowolnego obiektu. Wszelkie inne atrybucje XML zależą od Ciebie.

Po prostu użyj tego w ten sposób:

        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
źródło
2

Lub możesz dodać tę metodę do swojego obiektu:

    public void Save(string filename)
    {
        var ser = new XmlSerializer(this.GetType());
        using (var stream = new FileStream(filename, FileMode.Create))
            ser.Serialize(stream, this);
    }
Bigjim
źródło
1

Oto podstawowy kod, który pomoże serializować obiekty C # do 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
źródło
6
Byłoby miło, gdyby zacytował źródło tego kodu: 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)

Musisz użyć następujących klas:

using System.IO;
using System.Xml;
using System.Xml.Serialization;
Sagar Timalsina
źródło