Серіалізувати об’єкт у 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-представлення цього об’єкта?



1
Я створив просту бібліотеку, щоб досягти цього: github.com/aishwaryashiva/SaveXML
Aishwarya Shiva

Відповіді:


511

Ви повинні використовувати 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
     }
 }

10
Здається, працює бездоганно без лініїXmlWriter writer = XmlWriter.Create(sww);
Пол Хант

15
Для форматування серіалізованого об'єкта зробіть: XmlTextWriter writer = new XmlTextWriter(sww) { Formatting = Formatting.Indented };замістьXmlWriter writer = XmlWriter.Create(sww);
Tono Nam

4
Оскільки XmlWriterінкапсулює, StringWriterвам не потрібно утилізувати обидва (перше використання є зайвим), правда? Я припускаю, що XmlWriterпіклується про його розпорядження ...
говорить

4
@talles XmlWriterне інкапсулює StringWriter, він використовує ваш переданий текст StringWriterі не має ніяких сподівань / відповідальності розпоряджатися ним. Далі StringWriterвиходить за XmlWriterмежі сфери діяльності, ви все одно хочете, щоб вона XmlWriterперебувала утилізацією, було б поганою поведінкою, XmlWriterщоб розпоряджатися вашими StringWriter. Як правило, якщо ви декларуєте щось, що потребує розпорядження, ви відповідаєте за його утилізацію. І мається на увазі це правило, все, що ви не заявляєте про себе, не повинно розпоряджатися. Тож обидва usings необхідні.
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();

8
дуже приємне рішення, мені подобається, як ви реалізували це як метод розширення
Spyros

57
Тут я б запропонував одне: видалити блок спробувати ... catch. Це не приносить вам ніякої користі і просто приховує помилку, яку викидаєте.
варення

7
Чи вам також не потрібно користуватися струнарним текстом? наприклад: використання (var stringWriter = new StringWriter ())
Стівен Швидкий

3
@jammycakes Ні! Коли ви кидаєте нове Exceptionтуди, ви розширили StackTrace методом "Серіалізувати <>".
користувач11909

1
@ user2190035 напевно, якби перервати в методі розширення, слід стека починається там? "Розширення сліду стека" із спробою здається непотрібним?
ЛеРой

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
Мій досвід виявив, що це неправда. З більшими даними, використовуючий оператор розпоряджається потоком до очищення буфера. Я на 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;
    }
}

Ви можете зателефонувати цьому у клієнта.


21

Щоб серіалізувати об’єкт, виконайте:

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

Також пам’ятайте, що для роботи XmlSerializer потрібен конструктор без параметрів.


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
Так, можливо. Ви можете уникнути цього, записавши налаштування у тимчасовий файл, а потім замінивши оригінал.
Томаш

19

Усі вищевикладені відповіді правильні. Це просто найпростіша версія:

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

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 у потрібному місці.


4

мій робочий код. Повертає utf8 xml включення порожнього простору імен.

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

Приклад повернення відповіді Yandex api для оплати URL Aviso:

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

У мене є простий спосіб серіалізувати об’єкт до 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();
            }
        }
    }
}

2

Або ви можете додати цей метод до свого об’єкта:

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

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;
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.