【问题标题】:Serialization for document storage文档存储的序列化
【发布时间】:2011-01-14 01:58:02
【问题描述】:

我编写了一个可以打开/编辑/保存文档的桌面应用程序。

这些文档由几个不同类型的对象描述,这些对象存储彼此的引用。当然有一个Document 类作为这个数据结构的根。

问题是如何将此文档模型保存到文件中。

我需要什么:

  • 支持递归结构。
  • 它必须能够打开文件,即使它们是由稍微不同的类生成的。我的用户不想因为我在某处添加了一个字段而在每次发布后重新创建每个文档。
  • 它必须处理编译时未知的类(用于插件支持)。

到目前为止我累了什么:

  • XmlSerializer -> 不符合第一个和最后一个条件。
  • BinarySerializer -> 不符合第二个条件。

  • DataContractSerializer:类似于 XmlSerializer,但支持循环(递归)引用。此外,它的设计考虑了(向前/向后)兼容性:Data Contract Versioning。 [编辑]

  • NetDataContractSerializer:虽然 DataContractSerializer 仍然需要提前知道所有类型(即它不能很好地使用继承),但 NetDataContractSerializer 将类型信息存储在输出中。除此之外,两者似乎是等价的。 [编辑]

  • protobuf-net: 还没来得及试验,不过功能上看起来和DataContractSerializer差不多,不过是用二进制格式。 [编辑]

未知类型的处理[编辑]

当静态类型和动态类型不同时(如果你有一个 object 类型的字段,但其中有一个 Person-object),似乎有两种哲学。基本上动态类型必须以某种方式存储在文件中。

  • 对不同的动态类型使用不同的 XML 标记。但是由于要用于特定类的 XML 标记可能不等于类名,因此只有在反序列化器事先知道所有可能的类型(以便他可以扫描它们的属性)时,才可能走这条路。

  • 在序列化期间存储 CLR 类型(类名、程序集名称和版本)。在反序列化期间使用此信息来实例化正确的类。在反序列化之前必须不知道这些类型。

第二个更易于使用,但生成的文件将依赖于 CLR(并且对代码修改不太敏感)。这可能就是XmlSerializerDataContractSerializer 选择第一种方式的原因。不推荐NetDataContractSerializer,因为它使用了第二种方法(顺便说一下,BinarySerializer 也是如此)。

有什么想法吗?

【问题讨论】:

  • XmlSerializer 不会失败最后一个条件。只需在构造函数中传递额外的类型。如果你有一个插件架构,只需维护一个反映的具体类型的列表。

标签: c# .net serialization xml-serialization


【解决方案1】:

你没有尝试过的是DataContractSerializer。有一个构造函数接受一个参数 bool preserveObjectReferences 应该处理第一个条件。

【讨论】:

  • NetDataContractSerializer 符合所有三个要求。但是最后一个要求可能是一个坏主意,因为它与第二个要求相矛盾(我为此添加了“未知类型的处理”)。 DataContractSerializer 似乎是最佳选择。
【解决方案2】:

WCF 数据协定序列化程序可能最符合您的需求,尽管并不完美。

仅对向后兼容性的有限支持(即旧版本的程序是否可以读取使用新版本生成的文档)。支持新字段(通过 IExtensibleDataObject),但不支持新类或新枚举值。

【讨论】:

    【解决方案3】:

    我认为 XmlSerializer 是您最好的选择。如果没有在 Document 类中做一些工作,您将无法支持需求列表中的所有内容 - 但是 XmlSerializer 架构为您提供了可扩展点,应该允许您充分利用其机制来做任何事情。

    使用IXmlSerializable 接口——通过在你想要存储的类上实现它——你应该可以做任何事情,真的。

    接口基本上公开了两个方法——ReadXml 和 WriteXml

    public void WriteXml (XmlWriter writer)
    {
        // do what you need to do to write out your XML for this object
    }
    
    public void ReadXml (XmlReader reader)
    {
        // do what you need to do to read your object from XML
    }
    

    使用这两种方法,您应该能够从几乎任何您可能想要存储的对象中捕获必要的状态信息,并将其转换为可以持久保存到磁盘的 XML - 并在适当的时候反序列化回一个对象来了!

    【讨论】:

    • 好的,但是我怎样才能让递归使用这个接口工作呢?假设我有一些相互引用的对象:a->b->c->d->a
    【解决方案4】:

    XmlSerializer 可以满足您的第一个条件,但是您必须为 TreeView 控件等对象提供递归。

    BinaryFormatter 可以适用于所有 3 个标准。如果类发生变化,您可能必须创建一个转换工具来将旧格式文档转换为新格式。或者识别旧格式,反序列化为旧格式,然后保存为新格式 - 将旧的类格式保留一段时间。

    这将有助于涵盖 版本容差,这就是我认为您所追求的:MSDN - Version Tolerant Serialization

    【讨论】:

    • 转换工具听起来很干净。但是在开发过程中,当几分钟内发生变化时,这似乎是一个很大的开销。是否有可能有一些默认行为?像“忽略文件中不适合对象的字段”和“如果文件中缺少字段则添加默认值”。
    • 我知道你有像 [XmlIgnore] 这样的属性。我不确定二进制对应物是什么。您将不得不在这里查看如何装饰您的班级和成员:blog.kowalczyk.info/article/Serialization-in-C.html
    • 我认为等价的将是 [NonSerialized]。但我不明白这将如何帮助加载由早期版本的类编写的文件。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-15
    • 2016-12-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多