Clojure 数据结构序列化

2022-09-03 01:07:05

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

我的要求是:

  • 某种形式的人类可读文本格式(我可能更喜欢s表达式,JSON和XML的顺序,但对其他人开放)
  • 支持所有常用的Clojure数据结构,关键字和基元
  • 能够为自定义Java类,defrecords等提供自定义序列化/反序列化函数(这很重要,因为我需要在几种情况下做一些像Java的readResolve这样的事情)
  • 良好的性能是值得拥有的

有什么好的建议吗?


答案 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?=

要在未提供默认实现的情况下使用它,请实现 multimethod 。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

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

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


答案 2

edn格式现在已经作为使用Clojure的数据结构进行数据传输的标准发布。

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