【问题标题】:Clojure data structure serializationClojure 数据结构序列化
【发布时间】:2010-07-21 16:14:05
【问题描述】:

我有一个复杂的 Clojure 数据结构,我想序列化 - 基本上是我正在开发的在线游戏的整个当前游戏状态,以便我可以实现保存游戏文件。

我的要求是:

  • 某种形式的人类可读文本格式(我可能更喜欢按顺序排列的 s 表达式、JSON 和 XML,但对其他人开放)
  • 支持所有常用的 Clojure 数据结构、关键字和原语
  • 能够为自定义 java 类、defrecords 等提供自定义序列化/反序列化功能(这很重要,因为在某些情况下我需要执行 Java 的 readResolve 之类的操作)
  • 良好的性能是值得拥有的

有什么好的推荐吗?

【问题讨论】:

  • 在对 Clojure 了解不多的情况下,是否有理由使用从 Clojure 调用的标准 Java 序列化机制无法实现这一点?
  • @Gian - 是的,这当然是可能的,但我正在尝试学习做事的“Clojure 方式”:-)
  • IMO Clojure 的方式是使用 Java 的工具,它们为所解决的问题提供良好的解决方案。 :-) Serializable 可能是短期存储/传输数据结构的好解决方案。话虽如此,我想对于这个用例,需要一种更适合长期存储的格式,这可能由print-dup 提供。 (例如,如果实现核心 Clojure 数据结构的类的结构发生变化,Serializable 可能会遇到问题;print-dup 可能不会。)

标签: java serialization clojure


【解决方案1】:

如果你想将事物序列化为 S 表达式,你可以使用 print-dup:

(binding [*print-dup* true] (println [1 2 3]))
; prints [1 2 3]

(defrecord Foo [x])
; => user.Foo
(binding [*print-dup* true] (println (Foo. :foo)))
; prints #=(user.Foo/create {:x :foo})

请注意,打印一个包含十个对单个向量的引用然后读回它的结构会为您提供一个包含十个独立(不是identical?)的数据结构,尽管在结构(=)向量方面是等效的.

要在没有提供默认实现的情况下使用它,请实现多方法clojure.core/print-dup

另外,Clojure 1.2 中的很多东西都是java.io.Serializable

(every? (partial instance? java.io.Serializable)
        [{1 2} #{"asdf"} :foo 'foo (fn [] :foo)])
; => true

(defrecord Foo [])
(instance? java.io.Serializable (Foo.))
; => true

请注意,您应该避免序列化运行时创建的fns——它们是一次性类的实例,具有奇怪的名称,无论如何在重新启动 JVM 后您将无法反序列化它们。通过 AOT 编译,fns 确实获得了自己的固定类名。

更新: 正如对该问题的评论中提到的,Serializable 最适合短期存储/传输数据,而print-dup 作为长期数据应该更稳健存储解决方案(跨多个版本的应用程序、Clojure 等工作)。原因是print-dup 不依赖于被序列化的类的结构(所以当向量实现从 Java 切换到 Clojure 的 deftype 时,今天的向量 print-dup'd 仍然是可读的)。

【讨论】:

  • 我认为print-dup 可能是此用例的最佳解决方案,请参阅我对问题的评论...如果保存的游戏可能变大,则始终可以压缩打印输出。跨度>
【解决方案2】:

edn-format 现已作为使用 Clojure 数据结构的数据传输标准发布。

它非常适合序列化 Clojure 数据结构/值 - 并且支持多种语言,因此也可以用作数据交换格式。

【讨论】:

  • 这适用于核心 Clojure 数据结构,但假设您有一个更奇特的数据结构,例如 PriorityMap(看起来就像 PersistentMap 但行为不同),那么您需要使用阅读器宏还是正确的?
  • 他们虽然在 edn 格式中这样做了:您可以“标记”值以指示它们是特殊类型并编写您自己的处理程序来构造适当的类。很聪明!
【解决方案3】:

如果一切都是 Clojure 数据结构,那么它已经被序列化了(b/c of​​ codedata)。只需将数据结构转储到磁盘上。要恢复,请将它们加载回来并(评估)。

【讨论】:

  • *out*绑定到文件流并使用(pr)(然后使用(load-file)读回)。有关工作示例,请参阅 groups.google.com/group/clojure/browse_thread/thread/…
  • 评估它们不危险吗?有人可能会在那里偷偷一些代码。我相信它们应该被阅读而不是被评估。
  • 嗯,这是任何序列化的问题。如果未评估,则必须添加整个读取/解析层。如果这是一个不受信任的用户可以访问该文件的情况,那么我想我更愿意扩展它并放入加密层或哈希。
【解决方案4】:

对于 JSON,您可以使用标准 clojure-contrib.json。虽然,我记得,所有 Clojure 对象都应该是可序列化的......

【讨论】:

  • 试过了,它不喜欢我的 Java 类:它抛出例如java.lang.Exception:不知道如何编写 mikera.persistent.SparseMap 类的 JSON。有什么方法可以为您自己的类提供自定义序列化功能?
  • 你可以让你的类实现clojure.contrib.json.Write_JSON接口(或者协议clojure.contrib.json/Write-JSON,如果类是Clojure记录/类型)。不过,不确定您将如何阅读它们;我认为对于非标准数据结构,您需要使用 YAML 之类的东西。一个快速的谷歌搜索发现clj-yamlgithub.com/lancepantz/clj-yaml 作为一个可能的解决方案,虽然我对这个项目一无所知。
  • 啊,刚刚注意到clj-yaml的README说它现在只支持反序列化...
猜你喜欢
  • 2014-09-07
  • 2016-05-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-27
  • 2010-09-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多