views:

214

answers:

3

I have a complex Clojure data structure that I would like to serialize - basically the entire current game state for an online game I am developing so that I can implement save game files.

My requirements are:

  • Some form of human-readable text format (I'd probably prefer s-expressions, JSON and XML in that order but open to others)
  • Support all the usual Clojure data structures, keywords and primitives
  • Ability to provide custom serialization / deserialization functions for custom java classes, defrecords etc. (this is important because I need to do something like Java's readResolve in several cases)
  • Good performance is a nice-to-have

Any good recommendations?

+1  A: 

for JSON you can use standard clojure-contrib.json. Although, as I remember, all Clojure objects should be serializable...

Alex Ott
Tried this, it doesn't like my Java classes: it throws e.g. java.lang.Exception: Don't know how to write JSON of class mikera.persistent.SparseMap. Is there any way to give it custom serialisation functions for your own classes?
mikera
You could have your class implement the `clojure.contrib.json.Write_JSON` interface (or the protocol `clojure.contrib.json/Write-JSON`, if the class is a Clojure record / type). Not sure how you'd go about reading them back, though; I think for non-standard data structures you'd want to use something like YAML. A quick Google search finds `clj-yaml` http://github.com/lancepantz/clj-yaml as a possible solution, though I don't really know anything about the project.
Michał Marczyk
Ah, just noticed that `clj-yaml`'s README says it only supports deserialization for now...
Michał Marczyk
A: 

If everything is a Clojure data structure, then it's already serialized (b/c of code<->data). Just dump the data structures onto disk. To restore, load them back and (eval).

Greg Harman
+3  A: 

If you wanted to serialize things to S-expressions, you could use 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})

Note that printing a structure which holds, say, ten references to a single vector followed by reading it back gives you a datastructure with ten separate (not identical?), though equivalent in terms of structure (=) vectors.

To use this in cases where there is no default implementation provided, implement the multimethod clojure.core/print-dup.

Also, a lot of things in Clojure 1.2 are 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

Note that you should avoid serializing runtime-created fns -- they are instances of one-off classes with weird names and you won't be able to deserialize them after restarting your JVM anyway. With AOT compilation, fns do get their own fixed classnames.

Update: As mentioned in a comment on the question, Serializable is best suited to short-term storage / transfer of data, whereas print-dup should be more robust as a long-term storage solution (working across many versions of the application, Clojure etc.). The reason is that print-dup doesn't in any way depend on the structure of the classes being serialized (so a vector print-dup'd today will still be readable when the vector implementation switches from Java to Clojure's deftype).

Michał Marczyk
I think that `print-dup` might be the superior solution for this use case, see my comment on the question... If the save games may become large, the printouts can always be compressed.
Michał Marczyk