Serialize objek ke string

311

Saya memiliki metode berikut untuk menyimpan Objek ke file:

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

Saya akui saya tidak menulisnya (saya hanya mengonversinya menjadi metode ekstensi yang mengambil parameter tipe).

Sekarang saya membutuhkannya untuk mengembalikan xml kepada saya sebagai string (daripada menyimpannya ke file). Saya melihatnya, tetapi saya belum menemukan jawabannya.

Saya pikir ini mungkin sangat mudah bagi seseorang yang akrab dengan benda-benda ini. Jika tidak, saya akan mencari tahu akhirnya.

Gunung berapi
sumber

Jawaban:

530

Gunakan a StringWriteralih - alih 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();
    }
}

Catatan, penting untuk digunakan toSerialize.GetType()daripada typeof(T)dalam konstruktor XmlSerializer: jika Anda menggunakan yang pertama kode mencakup semua kemungkinan subkelas T(yang valid untuk metode ini), saat menggunakan yang terakhir akan gagal ketika melewati jenis yang berasal dari T. Berikut ini adalah tautan dengan beberapa contoh kode yang memotivasi pernyataan ini, dengan XmlSerializermelempar Exceptionkapan typeof(T)digunakan, karena Anda melewatkan instance dari tipe turunan ke metode yang memanggil SerializeObject yang didefinisikan dalam kelas dasar tipe turunan: http: // ideone .com / 1Z5J1 .

Juga, Ideone menggunakan Mono untuk mengeksekusi kode; sebenarnya ExceptionAnda akan mendapatkan menggunakan Microsoft .NET runtime memiliki yang berbeda Messagedari yang ditunjukkan pada Ideone, tetapi gagal sama saja.

dtb
sumber
2
@ JohnSaunders: ok, ide yang bagus untuk memindahkan diskusi ini di Meta. Berikut adalah tautan ke pertanyaan yang baru saja saya posting di Meta Stack Overflow mengenai pengeditan ini .
Fulvio
27
@casperOne Guys, tolong berhenti mengacaukan jawaban saya. Intinya adalah menggunakan StringWriter alih-alih StreamWriter, yang lainnya tidak relevan dengan pertanyaan. Jika Anda ingin membahas detail seperti typeof(T) versus toSerialize.GetType(), silakan lakukan, tetapi tidak dalam jawaban saya. Terima kasih.
dtb
9
@ dtb Maaf, tetapi Stack Overflow diedit secara kolaboratif . Juga, jawaban spesifik ini telah dibahas pada meta , jadi hasil edit tetap berlaku. Jika Anda tidak setuju, maka harap balas kiriman tersebut pada meta tentang mengapa menurut Anda jawaban Anda merupakan kasus khusus dan tidak boleh diedit secara kolaboratif.
casperOne
2
Bagaimanapun, ini adalah contoh terpendek yang pernah saya lihat. +1
froggythefrog
13
StringWriter mengimplementasikan IDisposable, jadi harus dimasukkan dalam blok menggunakan.
TrueWill
70

Saya tahu ini bukan jawaban untuk pertanyaan, tetapi berdasarkan jumlah suara untuk pertanyaan dan jawaban yang diterima, saya curiga orang-orang benar-benar menggunakan kode untuk membuat serialisasi objek ke string.

Menggunakan serialisasi XML menambahkan sampah teks tambahan yang tidak perlu ke output.

Untuk kelas berikut

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

itu menghasilkan

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

Solusi yang lebih baik adalah dengan menggunakan serialisasi JSON (salah satu yang terbaik adalah Json.NET ). Membuat cerita bersambung suatu objek:

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

Untuk menghilangkan bakteri objek:

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

String JSON serial akan terlihat seperti:

{"UserId":0}
xhafan
sumber
4
Dalam hal ini Anda benar tetapi Anda sudah melihat dokumen XML besar dan dokumen JSON besar. Dokumen JSON sulit disiapkan. "Sampah" yang Anda bicarakan seperti ruang nama dapat ditekan. XML yang dihasilkan dapat sebersih JSON tetapi SELALU lebih siap sebagai JSON. Keterbacaan adalah keuntungan besar di atas JSON.
Herman Van Der Blom
2
Jika Anda mencari "psoner online json" secara online, Anda akan menemukan beberapa parser json online yang dapat memformat string json dengan cara yang lebih mudah dibaca manusia.
xhafan
9
@HermanVanDerBlom XML lebih mudah dibaca daripada JSON? Apa yang kamu merokok? Itulah salah satu keunggulan JSON terkuat dibandingkan XML: jauh lebih mudah dibaca karena rasio sinyal / noise yang lebih tinggi. Sederhananya, dengan JSON konten tidak tenggelam dalam sup tag!
Mason Wheeler
63

Serialize dan 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);
    }
MENGAKUI
sumber
15
+1 untuk juga menunjukkan cara deserialisasi, tidak seperti semua jawaban lainnya. Terima kasih!
deadlydog
6
Satu perubahan kecil adalah mengembalikan T daripada objek, dan melemparkan objek yang dikembalikan ke T dalam fungsi DeserializeObject. Dengan cara ini objek yang diketik sangat dikembalikan bukan objek generik.
deadlydog
Terima kasih @deadlydog, saya sudah memperbaiki.
ADM-IT
3
TextWriter memiliki fungsi Buang () yang harus dipanggil. Jadi, Anda lupa pernyataan Menggunakan.
Herman Van Der Blom
38

Catatan Keamanan Kode

Mengenai jawaban yang diterima , penting untuk menggunakan toSerialize.GetType()alih-alih typeof(T)dalam XmlSerializerkonstruktor: jika Anda menggunakan yang pertama kode mencakup semua skenario yang mungkin, sementara menggunakan yang terakhir kadang-kadang gagal.

Berikut ini adalah tautan dengan beberapa contoh kode yang memotivasi pernyataan ini, dengan XmlSerializermelemparkan Pengecualian saat typeof(T)digunakan, karena Anda meneruskan turunan dari tipe turunan ke metode yang memanggil SerializeObject<T>()yang didefinisikan dalam kelas dasar tipe turunan: http: // ideone .com / 1Z5J1 . Perhatikan bahwa Ideone menggunakan Mono untuk mengeksekusi kode: Pengecualian yang sebenarnya Anda dapatkan dengan menggunakan Microsoft .NET runtime memiliki pesan yang berbeda dari yang ditunjukkan pada Ideone, tetapi gagal sama saja.

Demi kelengkapan saya memposting sampel kode lengkap di sini untuk referensi di masa mendatang, kalau-kalau Ideone (tempat saya memposting kode) menjadi tidak tersedia di masa depan:

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();
    }
}
Fulvio
sumber
12
Anda juga harus melakukan using (StringWriter textWriter = new StringWriter() {}penutupan / pembuangan objek dengan benar.
Amicable
Saya sepenuhnya setuju dengan Anda @Amicable! Saya hanya mencoba untuk menjaga sampel kode saya sedekat mungkin dengan OP satu, untuk menyoroti titik saya yang semuanya tentang jenis objek. Ngomong-ngomong, ada baiknya untuk mengingat siapa pun bahwa usingpernyataan itu adalah teman terbaik bagi kita dan juga IDisposableobjek pelaksana kita;)
Fulvio
"Metode ekstensi memungkinkan Anda untuk" menambahkan "metode ke tipe yang ada tanpa membuat tipe turunan baru, mengkompilasi ulang, atau memodifikasi tipe asli." msdn.microsoft.com/en-us/library/bb383977.aspx
Adrian
12

2p saya ...

        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());
                }
            }
        }
oPless
sumber
+1 untuk menggunakan XmlWriterSettings (). Saya ingin XML serial saya tidak menyia-nyiakan ruang dengan hal-hal yang cukup cetak dan pengaturan Indent = false dan NewLineOnAttributes = false melakukan pekerjaan.
Lee Richardson
Terima kasih @LeeRichardson - Saya perlu melakukan yang sebaliknya, juga XmlWriter di bawah .net default ke UTF16 yang bukan apa yang saya tulis juga.
oPless
menggunakan kombinasi memorystream ini dan mendapatkannya melalui Encoding GetString akan menyertakan Preamble / BOM sebagai karakter pertama dalam string Anda. Lihat juga stackoverflow.com/questions/11701341/...
Jamee
@Jamee "Pengkodean = UTF8Encoding (false)" berarti jangan menulis BOM sesuai docs.microsoft.com/en-us/dotnet/api/… ... apakah ini telah mengubah perilaku sejak .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();
            }
        }
teapeng
sumber
1

Saya tidak dapat menggunakan metode JSONConvert yang disarankan oleh xhafan

Di .Net 4.5 bahkan setelah menambahkan referensi perakitan "System.Web.Extensions", saya masih tidak dapat mengakses JSONConvert.

Namun, setelah Anda menambahkan referensi Anda bisa mendapatkan string yang sama dengan menggunakan:

JavaScriptSerializer js = new JavaScriptSerializer();
string jsonstring = js.Serialize(yourClassObject);
Thomas Tiveron
sumber
2
Kelas JSONConvert ada dalam ruang nama NewtonSoft.Json. Buka manajer paket di dalam kamu VS dan kemudian unduh paket NewtonSoft.Json
Amir Shrestha
1

Saya merasa perlu membagikan kode yang dimanipulasi ini ke jawaban yang diterima - karena saya tidak punya reputasi, saya tidak dapat berkomentar ..

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

Petteri Kautonen
sumber
Saya tahu ini sudah tua, tetapi karena Anda memberikan jawaban yang sangat bagus, saya akan menambahkan komentar kecil, seolah-olah saya melakukan review kode pada PR: Anda harus memiliki kendala pada T saat menggunakan obat generik. Ini membantu menjaga hal-hal tetap rapi, dan tidak setiap objek dalam basis kode dan kerangka kerja yang dirujuk cocok untuk serialisasi
Frank R. Haugen
-1

Dalam beberapa kasus yang jarang terjadi, Anda mungkin ingin mengimplementasikan serialisasi String Anda sendiri.

Tapi itu mungkin ide yang buruk kecuali Anda tahu apa yang Anda lakukan. (mis. serialisasi untuk I / O dengan file batch)

Sesuatu seperti itu akan melakukan triknya (dan akan mudah diedit dengan tangan / batch), tetapi hati-hati bahwa beberapa pemeriksaan harus dilakukan, seperti nama itu tidak mengandung baris baru.

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

[C #]

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