【问题标题】:Serialize an object to string将对象序列化为字符串
【发布时间】:2011-01-26 22:22:11
【问题描述】:

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

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

我认为这对于熟悉这些对象的人来说可能真的很容易。如果没有,我最终会弄清楚的。

【问题讨论】:

    标签: c# string serialization xml-serialization


    【解决方案1】:

    序列化和反序列化 XML/JSON (SerializationHelper.cs):

    using Newtonsoft.Json;
    using System.IO;
    using System.Xml.Serialization;
    
    namespace MyProject.Helpers
    {
        public static class SerializationHelper
        {
            public static T DeserializeXml<T>(this string toDeserialize)
            {
                XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
                using (StringReader textReader = new StringReader(toDeserialize))
                {
                    return (T)xmlSerializer.Deserialize(textReader);
                }
            }
    
            public static string SerializeXml<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 DeserializeJson<T>(this string toDeserialize)
            {
                return JsonConvert.DeserializeObject<T>(toDeserialize);
            }
    
            public static string SerializeJson<T>(this T toSerialize)
            {
                return JsonConvert.SerializeObject(toSerialize);
            }
        }
    }
    

    【讨论】:

    • +1 还展示了如何反序列化,这与所有其他答案不同。谢谢!
    • 一个小的变化是返回 T 而不是对象,并在 DeserializeObject 函数中将返回的对象转换为 T。这样会返回强类型对象而不是通用对象。
    • 谢谢@deadlydog,我已经修好了。
    • TextWriter 有一个应该调用的 Dispose() 函数。所以你忘记了 Using 语句。
    【解决方案2】:

    我觉得我需要将这个被操纵的代码分享给接受的答案 - 因为我没有声誉,我无法发表评论..

    using System;
    using System.Xml.Serialization;
    using System.IO;
    
    namespace ObjectSerialization
    {
        public static class ObjectSerialization
        {
            // THIS: (C): https://stackoverflow.com/questions/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 有限制仿制药。它有助于保持整洁,并不是代码库和引用的框架中的每个对象都适合序列化
    【解决方案3】:

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

    【讨论】:

      【解决方案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();
                  }
              }
      

      【讨论】:

        【解决方案5】:

        在极少数情况下,您可能希望实现自己的字符串序列化。

        但这可能是一个的想法,除非你知道自己在做什么。 (例如,使用批处理文件对 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;
        }
        

        【讨论】:

          【解决方案6】:

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

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

          但是,一旦添加了引用,您就可以使用以下方法打印出相同的字符串:

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

          【讨论】:

          • JSONConvert 类位于 NewtonSoft.Json 命名空间中。去你VS中的包管理器,然后下载NewtonSoft.Json包
          【解决方案7】:

          使用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 派生的类型时,后一个将失败。 这是一个包含一些示例代码的链接,这些示例代码激发了这个语句,当使用 typeof(T) 时,XmlSerializer 抛出一个 Exception,因为您将派生类型的实例传递给调用在派生中定义的 SerializeObject 的方法类型的基类:http://ideone.com/1Z5J1

          另外,Ideone 使用 Mono 来执行代码;您使用 Microsoft .NET 运行时获得的实际 Exception 与 Ideone 上显示的 Message 不同,但同样失败。

          【讨论】:

          • @JohnSaunders:好的,将这个讨论转移到 Meta 上是个好主意。这是link to the question I just posted on Meta Stack Overflow regarding this edit
          • @casperOne 伙计们,请不要再搞乱我的回答了。关键是使用 StringWriter 而不是 StreamWriter,其他一切都与问题无关。如果您想讨论诸如typeof(T)toSerialize.GetType() 之类的详细信息,请这样做,但不在我的回答中。谢谢。
          • @dtb 对不起,Stack Overflow is collaboratively edited。另外,this specific answer has been discussed on meta,所以编辑成立。如果您不同意,请在 meta 上回复该帖子,说明您为什么认为您的答案是特殊情况并且不应该进行协作编辑。
          • Codewise,这是我见过的最短的例子。 +1
          • StringWriter 实现了 IDisposable,所以应该包含在 using 块中。
          【解决方案8】:

          我的 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,这也不是我要写的。
          • 使用这种内存流组合并通过 Encoding GetString 获取它将包含 Preamble/BOM 作为字符串中的第一个字符。另见stackoverflow.com/questions/11701341/…
          • @Jamee "Encoding = UTF8Encoding (false)" 表示不要按照docs.microsoft.com/en-us/dotnet/api/… 编写 BOM ... 自 .net4 以来这种行为是否发生了变化?
          • @oPless 你是对的,我误解了我提供的链接的答案
          【解决方案9】:

          我知道这并不是问题的真正答案,但根据问题的投票数和接受的答案,我怀疑人们实际上是在使用代码将对象序列化为字符串。

          使用 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}
          

          【讨论】:

          • 在这种情况下,您是对的,但是您是否见过大型 XML 文档和大型 JSON 文档。 JSON 文档难以阅读。您正在谈论的“垃圾”(例如名称空间)可以被抑制。生成的 XML 可以像 JSON 一样干净,但总是比 JSON 更具可读性。可读性是 JSON 的一大优势。
          • 如果您在网上搜索“json online parser”,您会发现一些在线 json 解析器,它们可以以更易读的方式格式化 json 字符串。
          • OP 需要 XML 而不是 JSON。这无关紧要。
          【解决方案10】:

          代码安全说明

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

          这里是一些示例代码的链接,其中包含激发此语句的一些示例代码,XmlSerializer 在使用 typeof(T) 时抛出异常,因为您将派生类型的实例传递给调用定义的 SerializeObject&lt;T&gt;() 的方法在派生类型的基类中: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();
              }
          }
          

          【讨论】:

          • 您还应该执行using (StringWriter textWriter = new StringWriter() {} 以正确关闭/处置对象。
          • 我完全同意你的看法@Amicable!我只是试图让我的代码示例尽可能接近 OP,以突出我的观点,即所有关于对象类型的观点。无论如何,记住using 语句是我们和我们亲爱的IDisposable 实现对象的最好朋友的任何人都很好;)
          • “扩展方法使您能够向现有类型“添加”方法,而无需创建新的派生类型、重新编译或以其他方式修改原始类型。” msdn.microsoft.com/en-us/library/bb383977.aspx
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-12-31
          • 2010-09-13
          • 2019-02-09
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多