将对象序列化为字符串


311

我有以下方法将对象保存到文件:

// 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作为字符串返回给我(而不是将其保存到文件中)。我正在研究它,但尚未弄清楚。

我认为这对于熟悉这些对象的人可能真的很容易。如果没有,我最终会解决。

Answers:


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

请注意,在XmlSerializer构造函数中使用toSerialize.GetType()而不是使用它很重要typeof(T):如果使用第一个,则代码将覆盖的所有可能的子类T(对于该方法有效),而使用后一个,则在传递从派生的类型时将失败T。这是一些示例代码的链接,这些代码可以激发该语句并XmlSerializer使用Exceptionwhen typeof(T),因为您将派生类型的实例传递给调用在派生类型的基类中定义的SerializeObject的方法:http:// ideone .com / 1Z5J1

另外,Ideone使用Mono执行代码;Exception使用Microsoft .NET运行时所得到的实际Message结果与Ideone上显示的结果有所不同,但是失败相同。


2
@JohnSaunders:好的,在Meta上进行讨论是个好主意。这是我刚刚在Meta Stack Overflow上发布的有关此编辑的问题链接
Fulvio 2012年

27
@casperOne伙计们,请不要再弄乱我的答案了。关键是使用StringWriter而不是StreamWriter,其他所有与该问题都不相关。如果您想讨论诸如typeof(T) vs的详细信息toSerialize.GetType(),请这样做,但不在我的回答中。谢谢。
dtb

9
@dtb对不起,但是Stack Overflow是经过协作编辑的。另外,已经在meta上讨论了这个特定的答案,因此编辑很有效。如果您不同意,请在meta上回复该帖子,说明为什么您认为答案是特例,因此不应进行协作编辑。
casperOne 2012年

2
在代码方面,这是我所见过的最短的示例。+1
froggythefrog13年

13
StringWriter实现IDisposable,因此应将其包含在using块中。
TrueWill 2014年

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

要反序列化对象:

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

序列化的JSON字符串如下所示:

{"UserId":0}

4
在这种情况下,您是对的,但是您已经看到大型XML文档和大型JSON文档。JSON文档几乎不可读。您正在谈论的“垃圾”(例如名称空间)可以被抑制。生成的XML可以像JSON一样干净,但始终像JSON一样可读。可读性是JSON之上的一大优势。
Herman Van Der Blom

2
如果您在网上搜索“ json在线解析器”,则会发现一些在线json解析器,它们可以以更易于理解的方式格式化json字符串。
xhafan

9
@HermanVanDerBlom XML比JSON更可读吗?您到底在吸烟什么?这是基于XML的JSON最强的优势之一:它是迄今为止比较容易,因为更高的信号/噪音比的阅读。简而言之,使用JSON,内容不会淹没在标签汤中!
梅森惠勒

63

序列化和反序列化(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表示与其他所有答案不同的是如何反序列化。谢谢!
致命狗2014年

6
不过,一个较小的更改是返回T而不是object,然后在DeserializeObject函数中将返回的对象转换为T。这样,将返回强类型对象而不是通用对象。
致命狗2014年

谢谢@deadlydog,我已经解决了。
ADM-IT

3
TextWriter具有应调用的Dispose()函数。因此,您忘记了Using语句。
Herman Van Der Blom

38

规范安全说明

关于公认的答案,重要的是要使用toSerialize.GetType()而不是typeof(T)XmlSerializer构造函数中使用:如果使用第一个,则代码涵盖所有可能的情况,而使用后一个则有时会失败。

这是一些示例代码的链接,这些代码可以激发该语句,并XmlSerializertypeof(T)使用时引发Exception ,因为您将派生类型的实例传递给调用SerializeObject<T>()该方法的方法,该方法在派生类型的基类中定义:http:// ideone .com / 1Z5J1请注意,Ideone使用Mono执行代码:使用Microsoft .NET运行时所获得的实际异常与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() {}正确关闭/处置对象。
2013年

我完全同意你@Amicable!我只是试图使我的代码示例尽可能地接近OP,以便强调我的观点,即所有关于对象类型的观点。无论如何,要记住任何人,该using声明对我们和我们亲爱的IDisposable实现对象都是最好的朋友;)
Fulvio 2014年

“扩展方法使您可以将方法“添加”到现有类型,而无需创建新的派生类型,重新编译或修改原始类型。” 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可以完成这项工作。
李·理查森

谢谢@LeeRichardson-我需要做相反的事情,.net下的XmlWriter也默认为UTF16,这不是我要写的。
oPless

使用内存流的这种组合并通过Encoding GetString进行获取,会将Preamble / BOM作为字符串中的第一个字符。另请参阅stackoverflow.com/questions/11701341/…–
Jamee

@Jamee“ Encoding = UTF8Encoding(false)”表示不按照docs.microsoft.com/en-us/dotnet/api/编写BOM 。.... 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

我无法使用xhafan建议的JSONConvert方法

在.Net 4.5中,即使添加了“ System.Web.Extensions”程序集参考之后,我仍然无法访问JSONConvert。

但是,添加引用后,您可以使用以下命令打印出相同的字符串:

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

2
JSONConvert类位于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);
            }
        }
    }
}


我知道这很老了,但是由于您给出了很好的答案,因此我将添加一条小注释,就像我对PR进行代码审查一样:使用泛型时,您应该对T施加约束。它有助于保持事物的整洁,并不是代码库和引用的框架中的每个对象都适合进行序列化
Frank R. Haugen

-1

在极少数情况下,您可能想实现自己的String序列化。

但这可能不是一个主意,除非您知道自己在做什么。(例如,使用批处理文件序列化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

[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;
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.