Nối tiếp một đối tượng thành chuỗi


311

Tôi có phương pháp sau để lưu Đối tượng vào tệp:

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

Tôi thú nhận rằng tôi đã không viết nó (tôi chỉ chuyển đổi nó thành một phương thức mở rộng có tham số kiểu).

Bây giờ tôi cần nó để trả lại xml cho tôi dưới dạng một chuỗi (thay vì lưu nó vào một tệp). Tôi đang xem xét nó, nhưng tôi chưa tìm ra.

Tôi nghĩ rằng điều này có thể thực sự dễ dàng cho một người quen thuộc với các đối tượng này. Nếu không tôi sẽ tìm ra nó cuối cùng.

Câu trả lời:


530

Sử dụng StringWriterthay vì 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();
    }
}

Lưu ý, điều quan trọng là sử dụng toSerialize.GetType()thay vì typeof(T)trong hàm tạo XmlSerializer: nếu bạn sử dụng lớp đầu tiên, mã sẽ bao gồm tất cả các lớp con có thể của T(có giá trị cho phương thức), trong khi sử dụng lớp sau sẽ thất bại khi chuyển một kiểu xuất phát từ T. Đây là một liên kết với một số mã ví dụ thúc đẩy tuyên bố này, với XmlSerializerviệc sử dụng Exceptionkhi nào typeof(T)được sử dụng, bởi vì bạn chuyển một thể hiện của loại dẫn xuất sang một phương thức gọi là serializeObject được định nghĩa trong lớp cơ sở của loại dẫn xuất: http: // ideone .com / 1Z5J1 .

Ngoài ra, Ideone sử dụng Mono để thực thi mã; thực tế Exceptionbạn sẽ nhận được bằng cách sử dụng Microsoft .NET runtime có khác Messagevới hiển thị trên Ideone, nhưng nó không thành công như nhau.


2
@JohnSaunders: ok, đó là một ý tưởng tốt để di chuyển cuộc thảo luận này trên Meta. Đây là liên kết đến câu hỏi tôi vừa đăng trên Meta Stack Overflow liên quan đến chỉnh sửa này .
Fulvio

27
@casperOne Các bạn ơi, làm ơn đừng làm phiền với câu trả lời của tôi. Vấn đề là sử dụng StringWriter thay vì StreamWriter, mọi thứ khác không liên quan đến câu hỏi. Nếu bạn muốn thảo luận chi tiết như typeof(T) so với toSerialize.GetType(), xin vui lòng làm như vậy, nhưng không phải trong câu trả lời của tôi. Cảm ơn.
dtb

9
@dtb Xin lỗi, nhưng Stack Overflow được cộng tác chỉnh sửa . Ngoài ra, câu trả lời cụ thể này đã được thảo luận trên meta , vì vậy phần chỉnh sửa đứng. Nếu bạn không đồng ý, thì vui lòng trả lời bài đăng đó trên meta về lý do tại sao bạn nghĩ câu trả lời của bạn là trường hợp đặc biệt và không nên chỉnh sửa cộng tác.
casperOne

2
Codewise, đây là ví dụ ngắn nhất tôi từng thấy. +1
froggythefrog

13
StringWriter thực hiện IDis Dùng một lần, do đó, nên được đặt trong một khối sử dụng.
TrueWill

70

Tôi biết đây không thực sự là một câu trả lời cho câu hỏi, nhưng dựa trên số phiếu bầu cho câu hỏi và câu trả lời được chấp nhận, tôi nghi ngờ mọi người thực sự đang sử dụng mã để nối tiếp một đối tượng thành một chuỗi.

Sử dụng tuần tự hóa XML thêm rác văn bản bổ sung không cần thiết vào đầu ra.

Dành cho lớp sau

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

nó tạo ra

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

Giải pháp tốt hơn là sử dụng tuần tự hóa JSON (một trong những cách tốt nhất là Json.NET ). Để tuần tự hóa một đối tượng:

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

Để khử lưu huỳnh một đối tượng:

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

Chuỗi JSON được tuần tự hóa sẽ trông như sau:

{"UserId":0}

4
Trong trường hợp này, bạn đúng nhưng bạn đã thấy các tài liệu XML lớn và các tài liệu JSON lớn. Tài liệu JSON khó đọc. "Rác" mà bạn đang nói giống như không gian tên có thể bị triệt tiêu. XML được tạo có thể rõ ràng như JSON nhưng LUÔN LUÔN dễ đọc hơn JSON. Khả năng đọc là một lợi thế lớn trên JSON.
Herman Van Der Blom

2
Nếu bạn tìm kiếm trực tuyến "json online Parser", bạn sẽ tìm thấy một số trình phân tích cú pháp json trực tuyến có thể định dạng chuỗi json theo cách dễ đọc hơn của con người.
xhafan

9
@HermanVanDerBlom XML dễ đọc hơn JSON? Bạn đang hút thuốc gì trên thế giới? Đó là một trong những ưu điểm mạnh nhất của JSON trên XML: đó là xa dễ dàng hơn để đọc vì tỷ lệ tín hiệu / tiếng ồn cao hơn. Nói một cách đơn giản, với JSON, nội dung không bị chìm trong súp tag!
Mason Wheeler

63

Tuần tự hóa và giải tuần tự hóa (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 cũng cho thấy cách khử lưu huỳnh, không giống như tất cả các câu trả lời khác. Cảm ơn!
deadlydog

6
Tuy nhiên, một thay đổi nhỏ sẽ là trả về T thay vì đối tượng và truyền đối tượng được trả về cho T trong hàm DeserializeObject. Bằng cách này, đối tượng gõ mạnh được trả về thay vì một đối tượng chung.
deadlydog

Cảm ơn @deadlydog, tôi đã sửa.
ADM-IT

3
TextWriter có hàm Dispose () nên được gọi. Vì vậy, bạn đang quên các câu lệnh sử dụng.
Herman Van Der Blom

38

Mã an toàn lưu ý

Về câu trả lời được chấp nhận , điều quan trọng là sử dụng toSerialize.GetType()thay vì typeof(T)trong hàm XmlSerializertạo: nếu bạn sử dụng câu đầu tiên, mã sẽ bao gồm tất cả các tình huống có thể xảy ra, trong khi sử dụng câu trả lời sau đôi khi không thành công.

Đây là một liên kết với một số mã ví dụ thúc đẩy tuyên bố này, với XmlSerializerviệc ném Ngoại lệ khi typeof(T)được sử dụng, bởi vì bạn chuyển một thể hiện của loại dẫn xuất sang một phương thức gọi SerializeObject<T>()được xác định trong lớp cơ sở của loại dẫn xuất: http: // ideone .com / 1Z5J1 . Lưu ý rằng Ideone sử dụng Mono để thực thi mã: Ngoại lệ thực tế bạn sẽ nhận được khi sử dụng Microsoft .NET runtime có Thông báo khác với thông báo hiển thị trên Ideone, nhưng nó không thành công như vậy.

Để hoàn thiện, tôi đăng mẫu mã đầy đủ ở đây để tham khảo trong tương lai, chỉ trong trường hợp Ideone (nơi tôi đã đăng mã) sẽ không khả dụng trong tương lai:

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
Bạn cũng nên làm using (StringWriter textWriter = new StringWriter() {}để đóng / xử lý đối tượng thích hợp.
Hòa giải

Tôi hoàn toàn đồng ý với bạn @Amossible! Tôi chỉ đơn giản là cố gắng giữ mẫu mã của mình càng gần càng tốt với OP, để làm nổi bật quan điểm của tôi, đó là tất cả về các loại đối tượng. Dù sao cũng tốt để nhớ bất cứ ai rằng usingtuyên bố là người bạn tốt nhất cho cả chúng tôi và cho các IDisposableđối tượng thực hiện thân yêu của chúng tôi ;)
Fulvio

"Các phương thức mở rộng cho phép bạn" thêm "các phương thức vào các loại hiện có mà không cần tạo một loại dẫn xuất mới, biên dịch lại hoặc sửa đổi loại ban đầu." msdn.microsoft.com/en-us/l Library / bb383977.aspx
Adrian

12

2p của tôi ...

        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 để sử dụng XmlWriterSinstall (). Tôi muốn XML được tuần tự hóa của mình không lãng phí dung lượng với các công cụ in đẹp và đặt Indent = false và NewLineOnAttribut = false đã thực hiện công việc.
Lee Richardson

Cảm ơn @LeeRichardson - Tôi cần phải làm ngược lại, XmlWriter trong .net mặc định là UTF16, đây không phải là điều tôi viết ra.
oPless

sử dụng kết hợp bộ nhớ này và nhận nó thông qua Encoding GetString sẽ bao gồm Preamble / BOM làm char đầu tiên trong chuỗi của bạn. Xem thêm stackoverflow.com/questions/11701341/
Mạnh

@Jamee "Encoding = UTF8Encoding (false)" có nghĩa là không viết BOM theo docs.microsoft.com/en-us/dotnet/api/, ... điều này có thay đổi hành vi kể từ .net4 không?
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

Tôi không thể sử dụng phương thức JSONConvert được đề xuất bởi xhafan

Trong .Net 4.5 ngay cả sau khi thêm tham chiếu lắp ráp "System.Web.Extensions", tôi vẫn không thể truy cập JSONConvert.

Tuy nhiên, khi bạn thêm tham chiếu, bạn có thể nhận được cùng một chuỗi in ra bằng cách sử dụng:

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

2
Lớp JSONConvert nằm trong không gian tên NewtonSoft.Json. Chuyển đến trình quản lý gói trong bạn VS và sau đó tải xuống gói NewtonSoft.Json
Amir Shrestha

1

Tôi cảm thấy giống như tôi cần chia sẻ mã bị thao túng này cho câu trả lời được chấp nhận - vì tôi không có tiếng tăm, tôi không thể bình luận ..

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ôi biết điều này đã cũ, nhưng vì bạn đã đưa ra một câu trả lời thực sự tốt, tôi sẽ thêm một nhận xét nhỏ, như thể tôi đã đánh giá mã về PR: Bạn nên có những ràng buộc đối với T khi sử dụng thuốc generic. Nó giúp giữ cho mọi thứ gọn gàng và không phải mọi đối tượng trong cơ sở mã và các khung được tham chiếu đều cho vay để tuần tự hóa
Frank R. Haugen

-1

Trong một số trường hợp hiếm hoi, bạn có thể muốn thực hiện tuần tự hóa Chuỗi của riêng mình.

Nhưng đó có lẽ là một ý tưởng tồi trừ khi bạn biết bạn đang làm gì. (ví dụ: tuần tự hóa cho I / O với một tệp bó)

Một cái gì đó như thế sẽ thực hiện thủ thuật (và có thể dễ dàng chỉnh sửa bằng tay / lô), nhưng hãy cẩn thận rằng một số kiểm tra khác cần được thực hiện, như tên đó không chứa dòng mới.

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

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