【问题标题】:How to serialize classes that were not designed to be serialized?如何序列化未设计为序列化的类?
【发布时间】:2009-05-07 00:04:12
【问题描述】:

我需要将一些类和数据结构保存到一个文件中。我的第一个反应是使用 XML 或二进制序列化,但这变成了一场噩梦。我有一组不打算序列化的类(私有设置器,没有无参数构造函数,没有序列化属性,字典等)。考虑到我无法更改这些课程,我该怎么办?是否有任何解决方法并且仍然使用序列化?

我是否必须编写所有代码来编写属性、集合等?

【问题讨论】:

  • 好问题,您能否详细说明一下当前问题的性能要求和规模。
  • 此时,我真的没有任何要求。我正在寻找最优雅的解决方案。
  • 你不能真正为零需求问题设计一个解决方案:) 无论你选择什么解决方案,它都可能是错误的解决方案。如果没有要求,真的没有办法回答这个问题......
  • @Sam Saffron,当我看到这个问题时,我有一个明确的要求:如何将TFS工作项存储在本地文件中,以便在离线时访问它们的基本属性,或者网络中断?

标签: c# xml-serialization


【解决方案1】:

使用 JavaScriptSerializer。它位于 System.Web.Script.Serialization 命名空间中,并在 System.Web.Extensions.dll 程序集中的 3.5 框架中实现。

使用这个类,您可以序列化任何 POCO,无论它是否标记为 [Serializable]。您的程序不必是 Web 应用程序即可使用 JSON 序列化。这是一个例子:

public class Unserializable
{
  public int Age { get; set; }
  public int ID { get; set; }
  public string Name { get; set; }
}

public class Program
{
  static void Main()
  {
    var u = new Unserializable
            {
              Age = 40,
              ID = 2,
              Name = "Betty"
            };
    var jser = new JavaScriptSerializer();
    var jsonText = jser.Serialize( u );
    // next line outputs {"Age":40,"ID":2,"Name":"Betty"}
    Console.WriteLine( jsonText );
  }
}

【讨论】:

  • 是的,它也处理字段。试试这个: public class Unserializable { public int Age;公共 int ID { 获取;放; } 公共字符串名称 { 获取;放; } public Unserializable(int age, int id, string name) { Age = age;身份证=身份证;姓名=姓名; } } var u = new Unserializable( 40, 2, "Donna" ); var jser = new JavaScriptSerializer(); var jsonText = jser.Serialize(u); Console.WriteLine("序列化 {0} 使用 " + "JavaScriptSerializer 变成 {1}:", u.GetType().Name, jsonText);这会将包含公共字段的对象的 JSON 文本输出为 '{"Age":40,"ID":2,"Name":"Donna"}'。
【解决方案2】:

听起来像是……序列化代理的工作!

转到http://msdn.microsoft.com/en-us/magazine/cc188950.aspx

概览。

【解决方案3】:

我会使用代码生成工具(mygeneration、T4 等)来生成要序列化的 DTO...

【讨论】:

  • 代码生成的问题在于,如果类发生变化,您必须再次编写代码。这可能是也可能不是问题。
【解决方案4】:

不是一个简单的解决方法。

您可能只想对类中的字段(私有和公共)进行序列化/反序列化,然后递归地执行此操作。您可能需要使用反射来解决它们。不要反序列化属性,因为设置属性值时可能会产生副作用。您还必须递归地反序列化所有作为对象的字段。挑战在于知道在哪里停下来。

您必须对如何使用反射反序列化序列化对象做出一些重大猜测。我必须假设这些对象的某些字段包含对其他对象的引用,并且您还必须知道如何遍历所有这些。

此外,您必须调用参数化构造函数,并希望在使用反射设置字段时不会造成太大损害。

简而言之,您最好创建一组特殊用途的包装类,该类以最佳猜测形式从序列化格式重新创建您的专有类。然后可以使您的特殊用途类可序列化。这不会是一项简单的任务。

也就是说,代码生成可能能够简化识别类中所有字段的任务。

【讨论】:

    【解决方案5】:

    另一种选择是使用适配器模式。

    好消息是您不必更改原来的课程。 坏消息是,您最终编写的代码可能是第一个“不可更改”类中的两倍。

    您最终会得到 2 个新类:适配器和新的可序列化类。

    这个想法是适配器知道如何通过检查不可序列化的类来创建可序列化的类。换一种方式(可序列化到不可序列化),您可以再次使用适配器(当然,这里我假设您的私有设置器实际上是通过参数化构造函数设置的)。

    Here's the pattern in detail.

    【讨论】:

      【解决方案6】:

      这有两个独立的部分:

      1. 提取/设置数据
      2. 从文件(或其他位置)存储/读取它

      对于第一部分,如果您无法更改要序列化/反序列化的类,那么您实际上只需要通过反射进行深层复制。

      对于序列化,遍历存储其值的类的每个成员。

      使用您想要存储它的任何介质,例如 XML,并将这些值写入磁盘。

      对于反序列化,从文件中读取值,创建对象的新实例并使用反射设置所有值。

      如果您获取/设置所有成员,您将拥有一个处于相同状态的对象。 (如果您有复杂的成员,您可能需要递归遍历所有对象。)

      至于如何在磁盘上存储数据,xml 或二进制都可以。如果您想查看它并使其易于阅读,请使用 XML。 (我建议您在初次尝试时使用它,因为它会使调试变得更加容易。)

      【讨论】:

        【解决方案7】:

        这实际上取决于您的问题规模和您需要的性能。

        如果您有 1 个问题类,我只需编写一个可以序列化/反序列化该类的代理类。如果您谈论的是 100 多个类,您可能需要使用支持所有复杂性的序列化框架。没有无参数构造是一场噩梦,无论您编写或使用什么序列化框架,您都需要预先知道要传递给构造函数的参数。私有设置器/字典/列表等问题不大。

        我为媒体浏览器写了一个mini-serialization 框架,它是BSD。我的重点是性能,但我使用的技术可以适应您的问题。可以使用在 Marc 的protocol buffers 中使用的类似技术。

        如果性能根本不重要,一个相当简单的序列化框架可以使用反射相当快地编码,当你试图保持事物的性能时,事情会变得很困难。

        【讨论】:

          猜你喜欢
          • 2015-02-15
          • 1970-01-01
          • 2011-12-06
          • 1970-01-01
          • 2010-09-08
          • 1970-01-01
          • 1970-01-01
          • 2019-12-15
          • 1970-01-01
          相关资源
          最近更新 更多