5

我正在尝试使用 read 和 show 进行序列化/反序列化(这本身不是问题),但在某种意义上可以扩展数据类型可以扩展(但不能缩小)。

假设我有这种类型:

data Foo = { bar :: Int } deriving (Show, Read)

和清单:

foos = [Foo 1, Foo 2]

我可以轻松地将其反序列化为文件:

hPutStrLn fileHand . ppShow $ foos

然后我可以将它序列化回来:

!str <- hGetContents fileHand
let foosFromFile = fromMaybe [] $ (readMaybe :: String -> Maybe [Foo]) str

但是假设几个月后我想在 Foo 类型中添加一个“baz”字段。旧格式文件的直接序列化将不再适用于读取,我需要转换文件(我真的不想要)。

那么,是否有一种优雅的(没有在程序本身中放置明确的版本控制逻辑)的解决方案来仍然序列化文件中的数据,并用默认值填充缺失的字段?也许一些类型的技巧?

谢谢。

4

4 回答 4

10

这可能不是您想要的,因为您想避免显式版本控制,但我仍然想指出safecopy哪个是版本化序列化的首选解决方案,并且至少让它有点轻松。

我认为在支持添加任意数量的新字段的同时,没有任何方法可以使用默认值ShowRead实例,但是您当然可以手动编写自己的Read实例来处理丢失的记录字段。但是,我认为这比仅使用safecopy.

于 2013-08-16T06:27:52.153 回答
4

根据您的用例,您还可以使用Yesod 中的persistent将数据持久保存在数据库中。报价:

Persistent 遵循类型安全和简洁的声明性语法的指导原则。其他一些不错的功能是:

  • 与数据库无关。对 PostgreSQL、SQLite、MySQL 和 MongoDB 有一流的支持,正在开发实验性的 CouchDB 支持。
  • 由于本质上是非关系的,我们同时能够支持更多的存储层,并且不受连接导致的一些性能瓶颈的限制。
  • 处理 SQL 数据库的一个主要挫折来源是模式的更改。Persistent 可以自动执行数据库迁移。

在这些情况下, Persistent会为您处理数据更改:

对于以下情况,它将自动更改架构:

  • 字段的数据类型已更改。但是,如果无法翻译数据,数据库可能会反对此修改。
  • 添加了一个字段。但是,如果该字段不为空,没有提供默认值(我们稍后会讨论默认值)并且数据库中已经有数据,数据库将不允许这种情况发生。
  • 字段从非空转换为空。在相反的情况下,Persistent 将尝试转换,这取决于数据库的批准。
  • 添加了一个全新的实体。
于 2013-08-16T08:37:25.523 回答
3

希望能够更改数据布局同时仍然能够访问您的内容几乎是发明数据库管理系统决定性动机之一。您是否考虑过将数据放入一个简单的 SQLite 表中?对于您正在尝试做的事情来说,这可能有点矫枉过正,但它有一些优点:

  • 几乎可以肯定比基于文本的编码更有效。
  • 您仍然可以轻松地从应用程序外部读取它(例如,检查是否保存了正确的内容)。
  • “转换文件”现在相当于一个简单的 SQL 查询。
  • 如果您避免*用作列选择器,您的旧代码仍然可以读取由较新版本的应用程序创建的内容。(即,向前兼容性和向后兼容性)。
  • 或者,您可以阅读编写一些简单的样板代码,这些代码读取 DB 模式并为尚不存在的任何列提供默认值。

我不知道这是否适合您的情况,但值得考虑。

于 2013-08-16T07:47:39.427 回答
2

是的。只需添加一个多态字段:

data Foo a = { bar :: Int, extra :: a } deriving (Show, Read)

a然后使用必须可序列化的约束定义一个序列化实例:

instance (Serialize a) => Serialize (Foo a) where ...

当您不使用额外字段时,只需将 a()插入其中,因为它()可以简单地序列化(并且已经有一个Serialize实例)。

编辑:哎呀,刚刚意识到你在谈论漂亮的印刷。等效的解决方案是定义一个像这样的类型类:

class PrettyPrint a where
    pp :: a -> String

instance PrettyPrint () where
    pp () = ""

instance (PrettyPrint a) => PrettyPrint (Foo a) where
    pp = ... -- You fill this in
于 2013-08-16T06:23:02.167 回答