Сериализация объекта в XML

292

У меня есть класс C #, который я унаследовал. Я успешно "построил" объект. Но мне нужно сериализовать объект в XML. Есть ли простой способ сделать это?

Похоже, класс был настроен для сериализации, но я не уверен, как получить представление XML. Мое определение класса выглядит так:

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

Вот то, что я думал, что мог сделать, но это не работает:

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

Как я могу получить представление XML этого объекта?

user462166
источник
См. Также: Сериализация и десериализация XML в CodeProject
Бехзад Эбрахими,
1
Я разработал простую библиотеку для достижения этой цели: github.com/aishwaryashiva/SaveXML
Айшвария Шива

Ответы:

510

Вы должны использовать XmlSerializer для сериализации XML. Ниже приведен образец фрагмента.

 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
источник
10
Кажется, работает отлично без линииXmlWriter writer = XmlWriter.Create(sww);
Пол Хант
15
Для сериализации отформатированного объекта сделайте: XmlTextWriter writer = new XmlTextWriter(sww) { Formatting = Formatting.Indented };вместоXmlWriter writer = XmlWriter.Create(sww);
Тоно Нам
4
Поскольку XmlWriterинкапсулирует, StringWriterвам не нужно избавляться от обоих (первое использование избыточно), верно? Я предполагаю, что XmlWriterпозаботится о том, чтобы избавиться от него ...
Talles
4
@talles XmlWriterне инкапсулирует StringWriter, он использует ваш переданный StringWriterи не имеет никаких ожиданий / обязательств по его удалению. Кроме того, это StringWriterвыходит за XmlWriterрамки, вы можете все еще хотеть, когда XmlWriterвы его утилизируете, это будет плохим поведением, XmlWriterчтобы избавиться от вашего StringWriter. Как правило, если вы объявляете что-то, что требует утилизации, вы несете ответственность за его утилизацию. И подразумеваемое этим правилом, все, что вы не заявляете себе, вы не должны распоряжаться. Так что оба usingс необходимы.
Arkaine55
3
использование System.Xml.Serialization; используя System.IO; использование System.Xml;
тимофеевка
122

Я изменил мой, чтобы вернуть строку, а не использовать переменную ref, как показано ниже.

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

Его использование будет выглядеть так:

var xmlString = obj.Serialize();
Kwex
источник
8
очень хорошее решение, мне нравится, как вы реализовали это как метод расширения
Spyros
57
Здесь я бы предложил одно: уберите блок try ... catch. Это не дает вам никакой пользы, а просто запутывает ошибку, которая выдается.
jammycakes
7
Вам также не нужно использовать на струнном писателе? Например: использование (var stringWriter = new StringWriter ())
Стивен Квик
3
@jammycakes Нет! Когда вы Exceptionдобавляете туда новое , вы расширяете StackTrace методом «Serialize <>».
user11909
1
@ user2190035 наверняка, если бы он сломался в методе расширения, трассировка стека началась бы там? "Расширение трассировки стека" с попыткой кажется ненужным?
LeRoi
43

Следующая функция может быть скопирована в любой объект для добавления функции сохранения XML с использованием пространства имен 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();
    }
}

Чтобы создать объект из сохраненного файла, добавьте следующую функцию и замените [ObjectType] типом создаваемого объекта.

/// <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()избыточен в usingблоке - writer«s Dispose()метод будет вымывать его для вас.
Баваза
6
Мой опыт показал, что это не так. Для больших данных оператор using удалит поток до очистки буфера. Я на 100% рекомендую явно вызывать флеш.
Бен Грипка
6
writer.Flush () НЕ избыточен, он ДОЛЖЕН быть там. Без Flush может случиться так, что часть данных все еще находится в буфере StreamWriter, и файл удаляется, а некоторые данные отсутствуют.
Томас Кубес
Мне очень нравится ваш код: короткий и аккуратный. Моя проблема с копированием функций снова и снова в разные классы: не является ли это дублированием кода? Другие ответы предлагают общую библиотеку с методами расширения шаблонов, которые я бы принял. Что вы думаете?
Майкл Г
33

Класс расширения:

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

Использование:

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

string xml = foo.Serialize();

Просто обратитесь к пространству имен, содержащему ваш метод расширения, в файле, в котором вы хотите его использовать, и он будет работать (в моем примере это будет: using MyProj.Extensions; )

Обратите внимание, что если вы хотите сделать метод расширения специфичным только для определенного класса (например, Foo), вы можете заменить Tаргумент в методе расширения, например.

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

Александр Альберт
источник
31

Вы можете использовать функцию как ниже, чтобы получить сериализованный XML из любого объекта.

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

Вы можете позвонить это от клиента.

Imrul
источник
21

Чтобы сериализовать объект, выполните:

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

Также помните, что для работы XmlSerializer вам нужен конструктор без параметров.

Rox
источник
2
Это сводило меня с ума. Я не мог понять, почему это всегда было пустым. Потом понял, что у меня нет конструктора без параметров после прочтения вашего ответа. Спасибо.
Энди
19

Начну с копии ответа Бена Грипки:

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

Я использовал этот код ранее. Но реальность показала, что это решение немного проблематично. Обычно большинство программистов просто сериализуют настройки при сохранении и десериализуют настройки при загрузке. Это оптимистичный сценарий. После сбоя сериализации по какой-то причине файл частично записан, файл XML не завершен и является недействительным. В результате десериализация XML не работает, и ваше приложение может аварийно завершить работу при запуске. Если файл не очень большой, я предлагаю сначала сериализовать объект, а MemoryStreamзатем записать поток в файл. Это особенно важно в случае сложной пользовательской сериализации. Вы никогда не сможете проверить все случаи.

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

Сценарий десериализации в реальном мире следует считать поврежденным файлом сериализации, это случается когда-нибудь. Функция загрузки, предоставленная Беном Грипкой, в порядке.

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

И это может быть связано с некоторым сценарием восстановления. Подходит для файлов настроек или других файлов, которые могут быть удалены в случае проблем.

public static [ObjectType] LoadWithRecovery(string fileName)
{
    try
    {
        return Load(fileName);
    }
    catch(Excetion)
    {
        File.Delete(fileName); //delete corrupted settings file
        return GetFactorySettings();
    }
}
Томас Кубес
источник
Возможно ли прерывать процесс при записи MemoryStream в файл, например, при отключении питания?
Джон Смит
1
Да, это возможно. Вы можете избежать этого, записав настройки во временный файл, а затем заменив оригинал.
Томас Кубес
18

Все приведенные выше ответы верны. Это просто самая простая версия:

private string Serialize(Object o)
{
    using (var writer = new StringWriter())
    {
        new XmlSerializer(o.GetType()).Serialize(writer, o);
        return writer.ToString();
    }
}
AVJ
источник
9

Это немного сложнее, чем вызывать ToString метода класса, но не намного.

Вот простая функция, которую вы можете использовать для сериализации объектов любого типа. Возвращает строку, содержащую сериализованное содержимое 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;
    }
}
Коди Грей
источник
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();

Вы можете создать и сохранить результат в виде XML-файла в нужном месте.

Dev Try
источник
4

мой рабочий код. Возвращает utf8 xml enable пустое пространство имен.

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

Пример возврата ответа Яндекс.Папи: Ависо 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-сибирь
источник
4

У меня есть простой способ сериализации объекта в XML с использованием C #, он прекрасно работает и его можно многократно использовать. Я знаю, что это старая ветка, но я хотел опубликовать это, потому что кто-то может найти это полезным для них.

Вот как я называю метод:

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

Вот класс, который делает работу:

Примечание. Поскольку это методы расширения, они должны находиться в статическом классе.

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

Основываясь на вышеупомянутых решениях, здесь представлен класс расширения, который вы можете использовать для сериализации и десериализации любого объекта. Любые другие атрибуты XML зависят от вас.

Просто используйте это так:

        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
источник
2

Или вы можете добавить этот метод к вашему объекту:

    public void Save(string filename)
    {
        var ser = new XmlSerializer(this.GetType());
        using (var stream = new FileStream(filename, FileMode.Create))
            ser.Serialize(stream, this);
    }
Bigjim
источник
1

Вот основной код, который поможет сериализовать объекты C # в 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
Было бы неплохо, если вы приведете источник этого кода: 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)

Вам нужно использовать следующие классы:

using System.IO;
using System.Xml;
using System.Xml.Serialization;
Сагар Тималсина
источник