Tuần tự hóa một đối tượng thành XML


292

Tôi có một lớp C # mà tôi được thừa hưởng. Tôi đã "xây dựng" thành công đối tượng. Nhưng tôi cần tuần tự hóa đối tượng thành XML. Có một cách dễ dàng để làm điều đó?

Có vẻ như lớp đã được thiết lập để tuần tự hóa, nhưng tôi không chắc làm thế nào để có được biểu diễn XML. Định nghĩa lớp học của tôi trông như thế này:

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

Đây là những gì tôi nghĩ tôi có thể làm, nhưng nó không hoạt động:

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

Làm cách nào để có được biểu diễn XML của đối tượng này?



1
Tôi đã phát triển một thư viện đơn giản để đạt được điều này: github.com/aishwaryashiva/SaveXML
Aishwarya Shiva

Câu trả lời:


510

Bạn phải sử dụng XmlSerializer để tuần tự hóa XML. Dưới đây là một đoạn mẫu.

 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
Có vẻ hoạt động hoàn hảo mà không cần dây chuyềnXmlWriter writer = XmlWriter.Create(sww);
Paul Hunt

15
Để có đối tượng nối tiếp được định dạng, hãy làm: XmlTextWriter writer = new XmlTextWriter(sww) { Formatting = Formatting.Indented };thay vìXmlWriter writer = XmlWriter.Create(sww);
Tono Nam

4
XmlWriterđóng gói nên StringWriterbạn không cần loại bỏ cả hai (lần đầu tiên sử dụng là dư thừa), phải không? Tôi giả sử XmlWriterquan tâm đến việc xử lý nó ...
10/11/2015

4
@talles XmlWriterkhông gói gọn StringWriter, nó đang sử dụng thông qua của bạn StringWritervà không có kỳ vọng / trách nhiệm để loại bỏ nó. Hơn nữa StringWriterlà ngoài XmlWriterphạm vi, bạn vẫn có thể muốn nó khi XmlWriterđược xử lý, đó sẽ là hành vi kém XmlWriterđể xử lý của bạn StringWriter. Theo nguyên tắc chung, nếu bạn tuyên bố một cái gì đó cần xử lý, bạn có trách nhiệm xử lý nó. Và mặc nhiên theo quy tắc đó, bất cứ điều gì bạn không tuyên bố chính mình, bạn không nên vứt bỏ. Vì vậy, cả hai usingđều cần thiết.
Arkaine55

3
sử dụng System.Xml.Serialization; sử dụng System.IO; sử dụng System.Xml;
timothy

122

Tôi đã sửa đổi của tôi để trả về một chuỗi thay vì sử dụng một biến ref như bên dưới.

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

Cách sử dụng của nó sẽ như thế này:

var xmlString = obj.Serialize();

8
Giải pháp rất hay, tôi thích cách bạn thực hiện điều này như một phương pháp mở rộng
Spyros

57
Một điều tôi muốn đề xuất ở đây: xóa khối thử ... bắt. Nó không mang lại cho bạn bất kỳ lợi ích nào và chỉ làm xáo trộn lỗi đã bị ném.
jammycakes

7
Bạn cũng không cần sử dụng trên chuỗi ký tự? ví dụ: bằng cách sử dụng (var stringWriter = new StringWriter ())
Steven Quick

3
@jammycakes Không! Khi bạn ném một cái mới vào Exceptionđó, bạn đã mở rộng StackTrace bằng phương thức "Nối tiếp <>".
dùng11909

1
@ user2190035 chắc chắn nếu nó bị phá vỡ trong phương thức mở rộng thì dấu vết ngăn xếp sẽ bắt đầu từ đó? "Mở rộng dấu vết ngăn xếp" với thử có vẻ không cần thiết?
LeRoi

43

Hàm sau có thể được sao chép vào bất kỳ đối tượng nào để thêm hàm lưu XML bằng cách sử dụng không gian tên 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();
    }
}

Để tạo đối tượng từ tệp đã lưu, hãy thêm chức năng sau và thay thế [ObjectType] bằng loại đối tượng sẽ được tạo.

/// <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()là không cần thiết trong một usingkhối - writer's Dispose()phương pháp sẽ tuôn điều đó cho bạn.
bavaza

6
Kinh nghiệm của tôi đã tìm thấy điều đó không đúng. Với dữ liệu lớn hơn, câu lệnh sử dụng sẽ loại bỏ luồng trước khi bộ đệm bị xóa. Tôi 100% đề nghị rõ ràng gọi tuôn ra.
Ben Gripka 14/03/2015

6
nhà văn.Flush () KHÔNG dư thừa, nó PHẢI ở đó. Không có Flush, có thể xảy ra một phần dữ liệu vẫn nằm trong bộ đệm StreamWriter và tệp bị loại bỏ và một số dữ liệu bị thiếu.
Tomas Kubes

Tôi thích mã của bạn rất nhiều: ngắn và gọn gàng. Vấn đề của tôi là với việc sao chép các chức năng lặp đi lặp lại vào các lớp khác nhau: không phải là sao chép mã sao? Các câu trả lời khác đề xuất thư viện chung với các phương thức mở rộng mẫu mà tôi sẽ nắm lấy. Bạn nghĩ sao?
Michael G

33

Lớp mở rộng:

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

Sử dụng:

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

string xml = foo.Serialize();

Chỉ cần tham chiếu không gian tên giữ phương thức tiện ích mở rộng của bạn trong tệp bạn muốn sử dụng và nó sẽ hoạt động (trong ví dụ của tôi sẽ là using MyProj.Extensions;:)

Lưu ý rằng nếu bạn muốn làm cho phương thức mở rộng chỉ dành riêng cho một lớp cụ thể (ví dụ Foo:), bạn có thể thay thế Tđối số trong phương thức mở rộng, ví dụ:

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


31

Bạn có thể sử dụng hàm như dưới đây để nhận XML tuần tự từ bất kỳ đối tượng nào.

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

Bạn có thể gọi điều này từ khách hàng.


21

Để tuần tự hóa một đối tượng, hãy làm:

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

Cũng cần nhớ rằng để XmlSerializer hoạt động, bạn cần một hàm tạo không tham số.


2
Điều này đã khiến tôi phát điên. Tôi không thể hiểu tại sao nó luôn trống. Sau đó nhận ra tôi không có một nhà xây dựng không có tham số sau khi đọc câu trả lời của bạn. Cảm ơn bạn.
Andy

19

Tôi sẽ bắt đầu với câu trả lời sao chép của 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();
    }
}

Tôi đã sử dụng mã này trước đó. Nhưng thực tế cho thấy giải pháp này có một chút vấn đề. Thông thường hầu hết các lập trình viên chỉ cần tuần tự hóa cài đặt khi lưu và giải nén các cài đặt khi tải. Đây là một kịch bản lạc quan. Khi việc tuần tự hóa thất bại, vì một số lý do, tệp được ghi một phần, tệp XML không hoàn chỉnh và không hợp lệ. Do đó, quá trình khử lưu trữ XML không hoạt động và ứng dụng của bạn có thể bị sập khi bắt đầu. Nếu tệp không lớn, tôi đề nghị đối tượng tuần tự hóa đầu tiên MemoryStreamsau đó ghi luồng vào Tệp. Trường hợp này đặc biệt quan trọng nếu có một số tuần tự tùy chỉnh phức tạp. Bạn không bao giờ có thể kiểm tra tất cả các trường hợp.

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

Việc khử lưu huỳnh trong kịch bản thế giới thực sẽ được tính với tệp tuần tự bị hỏng, đôi khi nó xảy ra. Chức năng tải được cung cấp bởi Ben Gripka là tốt.

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

Và nó có thể được bao bọc bởi một số kịch bản phục hồi. Nó phù hợp cho các tệp cài đặt hoặc các tệp khác có thể bị xóa trong trường hợp có vấn đề.

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

Không thể quá trình bị gián đoạn trong khi ghi MemoryStream vào một tệp, ví dụ như tắt nguồn?
John Smith

1
Vâng, nó là có thể. Bạn có thể tránh nó bằng cách viết cài đặt vào một tệp tạm thời và sau đó thay thế bản gốc.
Tomas Kubes

18

Tất cả các câu trả lời nêu trên là chính xác. Đây chỉ là phiên bản đơn giản nhất:

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

9

Nó phức tạp hơn một chút so với việc gọi ToString phương thức của lớp, nhưng không nhiều.

Đây là một hàm thả xuống đơn giản mà bạn có thể sử dụng để tuần tự hóa bất kỳ loại đối tượng nào. Nó trả về một chuỗi chứa các nội dung XML được tuần tự hóa:

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

Bạn có thể tạo và lưu trữ kết quả dưới dạng tệp xml ở vị trí mong muốn.


4

mã công việc của tôi. Trả về utf8 xml cho phép không gian tên trống.

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

Ví dụ trả về phản hồi thanh toán 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

Tôi có một cách đơn giản để tuần tự hóa một đối tượng thành XML bằng C #, nó hoạt động rất tốt và nó có khả năng tái sử dụng cao. Tôi biết đây là một chủ đề cũ hơn, nhưng tôi muốn đăng bài này vì ai đó có thể thấy điều này hữu ích cho họ.

Đây là cách tôi gọi phương thức:

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

Đây là lớp thực hiện công việc:

Lưu ý: Vì đây là các phương thức mở rộng nên chúng cần nằm trong một lớp tĩnh.

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

Dựa trên các giải pháp trên, ở đây có một lớp mở rộng mà bạn có thể sử dụng để tuần tự hóa và giải tuần tự hóa bất kỳ đối tượng nào. Bất kỳ phân bổ XML nào khác là tùy thuộc vào bạn.

Chỉ cần sử dụng nó như thế này:

        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

Hoặc bạn có thể thêm phương thức này vào đối tượng của mình:

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

1

Đây là một mã cơ bản sẽ giúp tuần tự hóa các đối tượng C # thành 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
Sẽ thật tuyệt nếu bạn trích dẫn nguồn của mã này: support.microsoft.com/en-us/help/815813/
Kẻ

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)

Bạn cần sử dụng các lớp sau:

using System.IO;
using System.Xml;
using System.Xml.Serialization;
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.