ทำให้วัตถุเป็นสตริง


311

ฉันมีวิธีการดังต่อไปนี้เพื่อบันทึก Object เป็นไฟล์:

// Save an object out to the disk
public static void SerializeObject<T>(this T toSerialize, String filename)
{
    XmlSerializer xmlSerializer = new XmlSerializer(toSerialize.GetType());
    TextWriter textWriter = new StreamWriter(filename);

    xmlSerializer.Serialize(textWriter, toSerialize);
    textWriter.Close();
}

ฉันยอมรับว่าฉันไม่ได้เขียน (ฉันแปลงเป็นวิธีส่วนขยายที่ใช้พารามิเตอร์ประเภท)

ตอนนี้ฉันต้องการมันเพื่อให้ xml กลับมาให้ฉันเป็นสตริง (แทนที่จะบันทึกลงในไฟล์) ฉันกำลังมองหา แต่ฉันยังไม่ได้คิดออก

ฉันคิดว่ามันอาจจะง่ายสำหรับคนที่คุ้นเคยกับวัตถุเหล่านี้ ถ้าไม่ฉันจะคิดออกในที่สุด

คำตอบ:


530

ใช้StringWriterแทนStreamWriter:

public static string SerializeObject<T>(this T toSerialize)
{
    XmlSerializer xmlSerializer = new XmlSerializer(toSerialize.GetType());

    using(StringWriter textWriter = new StringWriter())
    {
        xmlSerializer.Serialize(textWriter, toSerialize);
        return textWriter.ToString();
    }
}

หมายเหตุมันเป็นสิ่งสำคัญที่จะใช้toSerialize.GetType()แทนtypeof(T)ใน XmlSerializer คอนสตรัค: ถ้าคุณใช้ครั้งแรกหนึ่งรหัสครอบคลุม subclasses ไปได้ทั้งหมดของT(ซึ่งเป็นที่ถูกต้องสำหรับวิธีการ) Tในขณะที่ใช้หลังหนึ่งจะล้มเหลวเมื่อผ่านชนิดที่ได้มาจาก นี่คือลิงค์ที่มีโค้ดตัวอย่างบางส่วนที่กระตุ้นให้เกิดคำสั่งนี้โดยมีXmlSerializerการใช้การโยนExceptionเมื่อtypeof(T)คุณส่งตัวอย่างของชนิดที่ได้รับไปยังวิธีการที่เรียก SerializeObject ที่กำหนดไว้ในคลาสพื้นฐานของประเภทที่ได้รับ: http: // ideone .com

นอกจากนี้ Ideone ยังใช้โมโนเพื่อรันโค้ด ของจริงที่Exceptionคุณจะได้รับจากการใช้ Microsoft .NET runtime นั้นแตกต่างMessageจากที่แสดงบน Ideone แต่มันก็ล้มเหลวเหมือนกัน


2
@JohnSaunders: ตกลงมันเป็นความคิดที่ดีที่จะย้ายการสนทนานี้ไปยัง Meta นี่คือลิงค์ไปยังคำถามที่ฉันเพิ่งโพสต์บน Meta Stack Overflow เกี่ยวกับการแก้ไขนี้
Fulvio

27
@casperOne Guys โปรดหยุดล้อเล่นกับคำตอบของฉัน จุดคือการใช้ StringWriter แทน StreamWriter ทุกอย่างอื่นไม่เกี่ยวข้องกับคำถาม หากคุณต้องการที่จะพูดคุยรายละเอียดเช่นtypeof(T) กับtoSerialize.GetType()โปรดทำเช่นนั้น แต่ไม่ใช่ในคำตอบของฉัน ขอบคุณ
dtb

9
@dtb ขออภัยกองมากเกินจะแก้ไขร่วมกัน นอกจากนี้คำตอบเฉพาะนี้ยังถูกกล่าวถึงในเมตาดังนั้นการแก้ไขจึงมีผล หากคุณไม่เห็นด้วยโปรดตอบกลับโพสต์ในเมตาว่าทำไมคุณคิดว่าคำตอบของคุณเป็นกรณีพิเศษและไม่ควรแก้ไขร่วมกัน
casperOne

2
Codewise นี่เป็นตัวอย่างที่สั้นที่สุดที่ฉันเคยเห็น +1
froggythefrog

13
StringWriter ใช้ IDisposable ดังนั้นควรอยู่ในบล็อกที่ใช้
TrueWill

70

ฉันรู้ว่านี่ไม่ใช่คำตอบสำหรับคำถาม แต่ตามจำนวนคะแนนโหวตสำหรับคำถามและคำตอบที่ยอมรับฉันสงสัยว่าผู้คนกำลังใช้รหัสเพื่อทำให้วัตถุเป็นสตริง

การใช้การทำให้เป็นอันดับ XML จะเพิ่มขยะพิเศษที่ไม่จำเป็นลงในเอาต์พุต

สำหรับคลาสต่อไปนี้

public class UserData
{
    public int UserId { get; set; }
}

มันสร้าง

<?xml version="1.0" encoding="utf-16"?>
<UserData xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
   <UserId>0</UserId>
</UserData>

ทางออกที่ดีกว่าคือการใช้การทำให้เป็นอันดับ JSON (หนึ่งในดีที่สุดคือJson.NET ) ในการทำให้วัตถุเป็นอนุกรม:

var userData = new UserData {UserId = 0};
var userDataString = JsonConvert.SerializeObject(userData);

ในการ deserialize วัตถุ:

var userData = JsonConvert.DeserializeObject<UserData>(userDataString);

สตริง JSON ที่ทำเป็นอนุกรมจะมีลักษณะดังนี้:

{"UserId":0}

4
ในกรณีนี้คุณพูดถูก แต่คุณเคยเห็นเอกสาร XML ขนาดใหญ่และเอกสาร JSON ขนาดใหญ่ เอกสาร JSON อ่านได้ยาก "ขยะ" ที่คุณกำลังพูดถึงเช่นเนมสเปซสามารถเก็บกดได้ XML ที่สร้างสามารถทำความสะอาดได้อย่างเดียวกับ JSON แต่สามารถอ่านได้ง่ายกว่าในฐานะ JSON ความสามารถในการอ่านเป็นข้อได้เปรียบเหนือ JSON
Herman Van Der Blom

2
หากคุณค้นหา "json ออนไลน์ parser" คุณจะพบ json parsers ออนไลน์ซึ่งสามารถจัดรูปแบบสตริง json ในรูปแบบที่มนุษย์อ่านได้มากขึ้น
xhafan

9
@HermanVanDerBlom XML อ่านง่ายกว่า JSON หรือไม่ คุณสูบบุหรี่ในโลกอะไร นั่นเป็นหนึ่งในข้อได้เปรียบที่แข็งแกร่ง JSON กว่า XML: มันไกลให้อ่านง่ายขึ้นเพราะของสัญญาณอัตราส่วนที่สูงขึ้น / เสียง พูดง่ายๆด้วย JSON เนื้อหาไม่จมน้ำในแท็กซุป!
Mason Wheeler

63

ทำให้เป็นอันดับและ Deserialize (XML / JSON):

    public static T XmlDeserialize<T>(this string toDeserialize)
    {
        XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
        using(StringReader textReader = new StringReader(toDeserialize))
        {      
            return (T)xmlSerializer.Deserialize(textReader);
        }
    }

    public static string XmlSerialize<T>(this T toSerialize)
    {
        XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
        using(StringWriter textWriter = new StringWriter())
        {
            xmlSerializer.Serialize(textWriter, toSerialize);
            return textWriter.ToString();
        }
    }

    public static T JsonDeserialize<T>(this string toDeserialize)
    {
        return JsonConvert.DeserializeObject<T>(toDeserialize);
    }

    public static string JsonSerialize<T>(this T toSerialize)
    {
        return JsonConvert.SerializeObject(toSerialize);
    }

15
+1 สำหรับการแสดงวิธี deserialize ซึ่งแตกต่างจากคำตอบอื่น ๆ ทั้งหมด ขอบคุณ!
deadlydog

6
หนึ่งการเปลี่ยนแปลงเล็กน้อยแม้ว่าจะส่งคืน T แทนวัตถุและส่งคืนวัตถุที่ถูกส่งคืนเป็น T ในฟังก์ชัน DeserializeObject วิธีนี้จะส่งคืนวัตถุที่พิมพ์อย่างมากแทนที่จะเป็นวัตถุทั่วไป
deadlydog

ขอบคุณ @deadlydog ฉันได้แก้ไขแล้ว
ADM-IT

3
TextWriter มีฟังก์ชัน Dispose () ที่ควรถูกเรียก ดังนั้นคุณจะลืมการใช้คำสั่ง
เฮอร์แมนแวนเดอ

38

หมายเหตุความปลอดภัยของรหัส

เกี่ยวกับคำตอบที่ยอมรับมันเป็นสิ่งสำคัญที่จะใช้toSerialize.GetType()แทนtypeof(T)ในตัวXmlSerializerสร้าง: ถ้าคุณใช้คนแรกที่รหัสครอบคลุมสถานการณ์ที่เป็นไปได้ทั้งหมดในขณะที่ใช้คนหลังล้มเหลวบางครั้ง

นี่คือลิงค์ที่มีโค้ดตัวอย่างบางส่วนที่กระตุ้นให้เกิดคำสั่งนี้โดยมีXmlSerializerการทิ้งข้อยกเว้นเมื่อtypeof(T)ใช้เพราะคุณผ่านอินสแตนซ์ของประเภทที่ได้รับไปยังวิธีการเรียกSerializeObject<T>()ที่กำหนดไว้ในคลาสพื้นฐานของประเภทที่ได้รับ: http: // ideone .com โปรดทราบว่า Ideone ใช้ Mono เพื่อเรียกใช้รหัส: ข้อยกเว้นจริงที่คุณจะได้รับจากการใช้ Microsoft .NET runtime มีข้อความที่แตกต่างจากที่แสดงบน Ideone แต่มันล้มเหลวเหมือนกัน

เพื่อความสมบูรณ์ฉันโพสต์ตัวอย่างโค้ดเต็มรูปแบบที่นี่สำหรับการอ้างอิงในอนาคตในกรณีที่Ideone (ที่ฉันโพสต์รหัส) จะไม่สามารถใช้งานได้ในอนาคต:

using System;
using System.Xml.Serialization;
using System.IO;

public class Test
{
    public static void Main()
    {
        Sub subInstance = new Sub();
        Console.WriteLine(subInstance.TestMethod());
    }

    public class Super
    {
        public string TestMethod() {
            return this.SerializeObject();
        }
    }

    public class Sub : Super
    {
    }
}

public static class TestExt {
    public static string SerializeObject<T>(this T toSerialize)
    {
        Console.WriteLine(typeof(T).Name);             // PRINTS: "Super", the base/superclass -- Expected output is "Sub" instead
        Console.WriteLine(toSerialize.GetType().Name); // PRINTS: "Sub", the derived/subclass

        XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
        StringWriter textWriter = new StringWriter();

        // And now...this will throw and Exception!
        // Changing new XmlSerializer(typeof(T)) to new XmlSerializer(subInstance.GetType()); 
        // solves the problem
        xmlSerializer.Serialize(textWriter, toSerialize);
        return textWriter.ToString();
    }
}

12
คุณควรทำusing (StringWriter textWriter = new StringWriter() {}เพื่อการปิด / กำจัดวัตถุอย่างเหมาะสม
กันเอง

ฉันเห็นด้วยอย่างสมบูรณ์กับคุณ @Amicable! ฉันพยายามเก็บตัวอย่างรหัสของฉันไว้ใกล้ที่สุดเท่าที่จะเป็นไปได้สำหรับ OP เพื่อเน้นจุดของฉันซึ่งทั้งหมดเกี่ยวกับประเภทของวัตถุ อย่างไรก็ตามมันดีที่จะจำทุกคนว่าusingคำแถลงนั้นเป็นเพื่อนที่ดีที่สุดทั้งกับเราและกับIDisposableวัตถุที่ใช้ของเรา;)
Fulvio

"ส่วนขยายวิธีการช่วยให้คุณสามารถ" เพิ่ม "วิธีการประเภทที่มีอยู่โดยไม่ต้องสร้างประเภทที่ได้รับใหม่การรวบรวมใหม่หรือการปรับเปลี่ยนประเภทเดิม" msdn.microsoft.com/en-us/library/bb383977.aspx
Adrian

12

ฉัน 2p ...

        string Serialise<T>(T serialisableObject)
        {
            var xmlSerializer = new XmlSerializer(serialisableObject.GetType());

            using (var ms = new MemoryStream())
            {
                using (var xw = XmlWriter.Create(ms, 
                    new XmlWriterSettings()
                        {
                            Encoding = new UTF8Encoding(false),
                            Indent = true,
                            NewLineOnAttributes = true,
                        }))
                {
                    xmlSerializer.Serialize(xw,serialisableObject);
                    return Encoding.UTF8.GetString(ms.ToArray());
                }
            }
        }

+1 สำหรับการใช้ XmlWriterSettings () ฉันต้องการ XML ที่ทำให้เป็นอนุกรมของฉันไม่ต้องเปลืองเนื้อที่ด้วยสิ่งที่พิมพ์สวยและการตั้งค่า Indent = false และ NewLineOnAttributes = false ทำงาน
Lee Richardson

ขอบคุณ @LeeRichardson - ฉันต้องทำสิ่งที่ตรงกันข้ามเช่น XmlWriter ภายใต้. net เริ่มต้นที่ UTF16 ซึ่งไม่ใช่สิ่งที่ฉันกำลังเขียน
oPless

การใช้หน่วยความจำแบบรวมกันและรับมันผ่านการเข้ารหัส GetString จะรวม Preamble / BOM เป็นอักขระตัวแรกในสตริงของคุณ ดูเพิ่มเติมที่stackoverflow.com/questions/11701341/…
Jamee

@Jamee "Encoding = UTF8Encoding (false)" หมายถึงไม่ต้องเขียน BOM ตามdocs.microsoft.com/en-us/dotnet/api/ ...... มีการเปลี่ยนแปลงพฤติกรรมนี้ตั้งแต่. net4 หรือไม่
oPless

4
public static string SerializeObject<T>(T objectToSerialize)
        {
            System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bf = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
            MemoryStream memStr = new MemoryStream();

            try
            {
                bf.Serialize(memStr, objectToSerialize);
                memStr.Position = 0;

                return Convert.ToBase64String(memStr.ToArray());
            }
            finally
            {
                memStr.Close();
            }
        }

        public static T DerializeObject<T>(string objectToDerialize)
        {
            System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bf = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
            byte[] byteArray = Convert.FromBase64String(objectToDerialize);
            MemoryStream memStr = new MemoryStream(byteArray);

            try
            {
                return (T)bf.Deserialize(memStr);
            }
            finally
            {
                memStr.Close();
            }
        }

1

ฉันไม่สามารถใช้วิธี JSONConvert ที่ xhafan แนะนำ

ใน. Net 4.5 แม้หลังจากเพิ่มการอ้างอิงชุดประกอบ "System.Web.Extensions" ฉันยังไม่สามารถเข้าถึง JSONConvert ได้

อย่างไรก็ตามเมื่อคุณเพิ่มการอ้างอิงคุณจะได้รับข้อความเหมือนเดิมโดยใช้:

JavaScriptSerializer js = new JavaScriptSerializer();
string jsonstring = js.Serialize(yourClassObject);

2
JSONConvert Class อยู่ใน NewtonSoft.Json เนมสเปซ ไปที่ผู้จัดการแพ็คเกจใน VS ของคุณแล้วดาวน์โหลดแพคเกจ NewtonSoft.Json
Amir Shrestha

1

ฉันรู้สึกเหมือนฉันต้องการแบ่งปันรหัสที่จัดการนี้กับคำตอบที่ยอมรับ - เนื่องจากฉันไม่มีชื่อเสียงฉันไม่สามารถแสดงความคิดเห็น ..

using System;
using System.Xml.Serialization;
using System.IO;

namespace ObjectSerialization
{
    public static class ObjectSerialization
    {
        // THIS: (C): /programming/2434534/serialize-an-object-to-string
        /// <summary>
        /// A helper to serialize an object to a string containing XML data of the object.
        /// </summary>
        /// <typeparam name="T">An object to serialize to a XML data string.</typeparam>
        /// <param name="toSerialize">A helper method for any type of object to be serialized to a XML data string.</param>
        /// <returns>A string containing XML data of the object.</returns>
        public static string SerializeObject<T>(this T toSerialize)
        {
            // create an instance of a XmlSerializer class with the typeof(T)..
            XmlSerializer xmlSerializer = new XmlSerializer(toSerialize.GetType());

            // using is necessary with classes which implement the IDisposable interface..
            using (StringWriter stringWriter = new StringWriter())
            {
                // serialize a class to a StringWriter class instance..
                xmlSerializer.Serialize(stringWriter, toSerialize); // a base class of the StringWriter instance is TextWriter..
                return stringWriter.ToString(); // return the value..
            }
        }

        // THIS: (C): VPKSoft, 2018, https://www.vpksoft.net
        /// <summary>
        /// Deserializes an object which is saved to an XML data string. If the object has no instance a new object will be constructed if possible.
        /// <note type="note">An exception will occur if a null reference is called an no valid constructor of the class is available.</note>
        /// </summary>
        /// <typeparam name="T">An object to deserialize from a XML data string.</typeparam>
        /// <param name="toDeserialize">An object of which XML data to deserialize. If the object is null a a default constructor is called.</param>
        /// <param name="xmlData">A string containing a serialized XML data do deserialize.</param>
        /// <returns>An object which is deserialized from the XML data string.</returns>
        public static T DeserializeObject<T>(this T toDeserialize, string xmlData)
        {
            // if a null instance of an object called this try to create a "default" instance for it with typeof(T),
            // this will throw an exception no useful constructor is found..
            object voidInstance = toDeserialize == null ? Activator.CreateInstance(typeof(T)) : toDeserialize;

            // create an instance of a XmlSerializer class with the typeof(T)..
            XmlSerializer xmlSerializer = new XmlSerializer(voidInstance.GetType());

            // construct a StringReader class instance of the given xmlData parameter to be deserialized by the XmlSerializer class instance..
            using (StringReader stringReader = new StringReader(xmlData))
            {
                // return the "new" object deserialized via the XmlSerializer class instance..
                return (T)xmlSerializer.Deserialize(stringReader);
            }
        }

        // THIS: (C): VPKSoft, 2018, https://www.vpksoft.net
        /// <summary>
        /// Deserializes an object which is saved to an XML data string.
        /// </summary>
        /// <param name="toDeserialize">A type of an object of which XML data to deserialize.</param>
        /// <param name="xmlData">A string containing a serialized XML data do deserialize.</param>
        /// <returns>An object which is deserialized from the XML data string.</returns>
        public static object DeserializeObject(Type toDeserialize, string xmlData)
        {
            // create an instance of a XmlSerializer class with the given type toDeserialize..
            XmlSerializer xmlSerializer = new XmlSerializer(toDeserialize);

            // construct a StringReader class instance of the given xmlData parameter to be deserialized by the XmlSerializer class instance..
            using (StringReader stringReader = new StringReader(xmlData))
            {
                // return the "new" object deserialized via the XmlSerializer class instance..
                return xmlSerializer.Deserialize(stringReader);
            }
        }
    }
}


ฉันรู้ว่ามันเก่า แต่เนื่องจากคุณให้คำตอบที่ดีจริงๆฉันจะเพิ่มความคิดเห็นเล็ก ๆ ราวกับว่าฉันได้ตรวจสอบรหัสในการประชาสัมพันธ์: คุณควรมีข้อ จำกัด ใน T เมื่อใช้ยาชื่อสามัญ มันช่วยรักษาสิ่งต่าง ๆ ให้เรียบร้อยและไม่ใช่ทุกวัตถุในโค้ด - เบสและกรอบอ้างอิงที่อ้างถึงในการทำให้เป็นอันดับ
Frank R. Haugen

-1

ในบางกรณีที่ไม่ค่อยเกิดขึ้นคุณอาจต้องการใช้การทำให้เป็นอันดับสตริงของคุณเอง

แต่นั่นอาจเป็นความคิดที่ไม่ดีเว้นแต่คุณจะรู้ว่าคุณกำลังทำอะไรอยู่ (เช่นการทำให้เป็นอนุกรมสำหรับ I / O พร้อมไฟล์แบตช์)

บางอย่างเช่นนั้นจะใช้กลอุบาย (และมันจะง่ายต่อการแก้ไขด้วยมือ / แบทช์) แต่โปรดระวังว่าควรทำการตรวจสอบเพิ่มเติมอีกบางอย่างเช่นชื่อนั้นไม่มีการขึ้นบรรทัดใหม่

public string name {get;set;}
public int age {get;set;}

Person(string serializedPerson) 
{
    string[] tmpArray = serializedPerson.Split('\n');
    if(tmpArray.Length>2 && tmpArray[0].Equals("#")){
        this.name=tmpArray[1];
        this.age=int.TryParse(tmpArray[2]);
    }else{
        throw new ArgumentException("Not a valid serialization of a person");
    }
}

public string SerializeToString()
{
    return "#\n" +
           name + "\n" + 
           age;
}

-1

[VB]

Public Function XmlSerializeObject(ByVal obj As Object) As String

    Dim xmlStr As String = String.Empty

    Dim settings As New XmlWriterSettings()
    settings.Indent = False
    settings.OmitXmlDeclaration = True
    settings.NewLineChars = String.Empty
    settings.NewLineHandling = NewLineHandling.None

    Using stringWriter As New StringWriter()
        Using xmlWriter__1 As XmlWriter = XmlWriter.Create(stringWriter, settings)

            Dim serializer As New XmlSerializer(obj.[GetType]())
            serializer.Serialize(xmlWriter__1, obj)

            xmlStr = stringWriter.ToString()
            xmlWriter__1.Close()
        End Using

        stringWriter.Close()
    End Using

    Return xmlStr.ToString
End Function

Public Function XmlDeserializeObject(ByVal data As [String], ByVal objType As Type) As Object

    Dim xmlSer As New System.Xml.Serialization.XmlSerializer(objType)
    Dim reader As TextReader = New StringReader(data)

    Dim obj As New Object
    obj = DirectCast(xmlSer.Deserialize(reader), Object)
    Return obj
End Function

[ค#]

public string XmlSerializeObject(object obj)
{
    string xmlStr = String.Empty;
    XmlWriterSettings settings = new XmlWriterSettings();
    settings.Indent = false;
    settings.OmitXmlDeclaration = true;
    settings.NewLineChars = String.Empty;
    settings.NewLineHandling = NewLineHandling.None;

    using (StringWriter stringWriter = new StringWriter())
    {
        using (XmlWriter xmlWriter = XmlWriter.Create(stringWriter, settings))
        {
            XmlSerializer serializer = new XmlSerializer( obj.GetType());
            serializer.Serialize(xmlWriter, obj);
            xmlStr = stringWriter.ToString();
            xmlWriter.Close();
        }
    }
    return xmlStr.ToString(); 
}

public object XmlDeserializeObject(string data, Type objType)
{
    XmlSerializer xmlSer = new XmlSerializer(objType);
    StringReader reader = new StringReader(data);

    object obj = new object();
    obj = (object)(xmlSer.Deserialize(reader));
    return obj;
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.