ทำให้เป็นอันดับวัตถุเป็น 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 น้ำ

4
เนื่องจากXmlWriterแค็ปซูลStringWriterคุณไม่จำเป็นต้องกำจัดทั้งสองอย่าง (การใช้ครั้งแรกซ้ำซ้อน) ใช่ไหม? ฉันสมมติว่าXmlWriterจะจัดการกับมัน ...
สูง

4
@talles XmlWriterไม่ได้ห่อหุ้มStringWriterมันคือการใช้ประโยชน์จากการส่งผ่านของคุณStringWriterและไม่มีความคาดหวัง / ความรับผิดชอบในการกำจัดมัน เพิ่มเติมStringWriterอยู่นอกXmlWriter's ขอบเขตคุณยังอาจต้องการมันเมื่อXmlWriterเป็นที่จำหน่ายก็จะมีพฤติกรรมที่ดีสำหรับการกำจัดของคุณXmlWriter StringWriterตามกฎทั่วไปหากคุณประกาศสิ่งที่ต้องการกำจัดคุณต้องรับผิดชอบต่อการกำจัดทิ้ง และโดยนัยต่อกฎนั้นสิ่งใดก็ตามที่คุณไม่ได้ประกาศตัวเองคุณไม่ควรทิ้ง ดังนั้นทั้งสองusingเป็นสิ่งที่จำเป็น
Arkaine55

3
ใช้ System.Xml.Serialization ใช้ System.IO; ใช้ System.Xml;
ทิโมธี

122

ฉันแก้ไขของฉันเพื่อส่งคืนสตริงแทนที่จะใช้ตัวแปรการอ้างอิงเช่นด้านล่าง

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 block มันไม่ได้ให้ประโยชน์ใด ๆ แก่คุณและเพียงทำให้งงงวยข้อผิดพลาดที่เกิดขึ้น
jammycakes

7
คุณไม่จำเป็นต้องใช้กับนักเขียนสตริงด้วยเหรอ? เช่น: using (var stringWriter = new StringWriter ())
Steven Quick

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()วิธีการจะล้างมันสำหรับคุณ
bavaza

6
ประสบการณ์ของฉันพบว่าไม่เป็นความจริง ด้วยข้อมูลที่ใหญ่ขึ้นข้อความการใช้จะกำจัดกระแสข้อมูลก่อนที่บัฟเฟอร์จะถูกล้าง ฉัน 100% แนะนำให้โทรหา flush อย่างชัดเจน
Ben Gripka

6
writer.Flush () ไม่ซ้ำซ้อนต้องอยู่ที่นั่น หากไม่มี Flush อาจเกิดขึ้นได้ว่าส่วนหนึ่งของข้อมูลยังคงอยู่ในบัฟเฟอร์ StreamWriter และไฟล์จะถูกกำจัดและข้อมูลบางอย่างขาดหายไป
Tomas Kubes

ฉันชอบรหัสของคุณมาก: สั้นและเรียบร้อย ปัญหาของฉันคือการคัดลอกฟังก์ชั่นซ้ำแล้วซ้ำอีกไปยังคลาสอื่น: มันไม่ซ้ำซ้อนรหัสหรือไม่ คำตอบอื่น ๆ แนะนำห้องสมุดทั่วไปด้วยวิธีการขยายเทมเพลตที่ฉันจะโอบกอด คุณคิดอย่างไร?
Michael G

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
นี่ทำให้ฉันบ้า ฉันไม่สามารถเข้าใจได้ว่าทำไมมันจึงว่างเปล่าเสมอ จากนั้นก็รู้ว่าฉันไม่มี Constructor ที่ไม่มีพารามิเตอร์หลังจากอ่านคำตอบ ขอบคุณ.
Andy

19

ฉันจะเริ่มต้นด้วยคำตอบการคัดลอกของ 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();
    }
}

ฉันใช้รหัสนี้ก่อนหน้านี้ แต่ความจริงแสดงให้เห็นว่าวิธีนี้เป็นปัญหาเล็กน้อย โดยทั่วไปโปรแกรมเมอร์ส่วนใหญ่มักจะตั้งค่าให้เป็นอนุกรมในการบันทึกและยกเลิกการตั้งค่าตามลำดับเมื่อโหลด นี่คือสถานการณ์ในแง่ดี เมื่อการทำให้เป็นอนุกรมล้มเหลวเนื่องจากสาเหตุบางประการไฟล์ถูกเขียนบางส่วนไฟล์ 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());
    }
}

การดีซีเรียลไลซ์เซชันในสถานการณ์จริงควรนับด้วยไฟล์การทำให้เป็นอนุกรมที่เสียหายซึ่งเกิดขึ้นบ้างในบางครั้ง ฟังก์ชั่นโหลดของ Ben Gripka นั้นใช้ได้

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 ไปยังไฟล์เช่นโดยการปิดเครื่อง?
John Smith

1
ใช่มันเป็นไปได้ คุณสามารถหลีกเลี่ยงได้โดยการเขียนการตั้งค่าเป็นไฟล์ชั่วคราวจากนั้นแทนที่ต้นฉบับ
โทมัส

19

คำตอบ upvoted ทั้งหมดข้างต้นถูกต้อง นี่เป็นเวอร์ชั่นที่ง่ายที่สุด:

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

ขึ้นอยู่กับการแก้ปัญหาข้างต้นที่นี่มาส่วนขยายระดับซึ่งคุณสามารถใช้ในการทำให้เป็นอันดับและ deserialize วัตถุใด ๆ การระบุ 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;
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.