3
repl> (-> root zip/down zip/right)
[{:answer-keys [5 6], :id 3} {:l [{:id 2, :answer-keys []}], :pnodes [{:answer-keys [2 3 4], :id 1}], :ppath nil, :r ({:answer-keys [7], :id 4})}]

I see this data when I print out the zipper on the repl. I am thinking that this may be all the data I would need to serialize a zipper? Is it possible to de-serialize a zipper from the provided data?

I am looking for something like the zip/serialize and zip/deserialize function imagined below.

(def s (zip/serialize (-> root zip/down zip/right))) ;; s is a string
(def d (zip/deserialize s)) ;; d is a zipper location
;;And I can go on using the deserialized zipper d without any difficulty.

Does anyone know how to do this?

Stephen Cagle
  • 14,124
  • 16
  • 55
  • 86

2 Answers2

4

the magic of zippers is they are a data structure that represents everything needed to produce arbitrarily modified versions of tree structures. zippers print and read just fine because they are proper values and don't require any state

you can "serialize" it with pr-str and "deserialize" it with read

make a zipper:

user> (zip/vector-zip [[1 [2]][3][4]])
[[[1 [2]] [3] [4]] nil]
user> (def s (zip/vector-zip [[1 [2]][3][4]]))
#'user/s
user> s
[[[1 [2]] [3] [4]] nil]

serialize it to a string:

user> (def serialized-s (pr-str (zip/next s)))
#'user/serialized-s
user> serialized-s
"[[1 [2]] {:l [], :pnodes [[[1 [2]] [3] [4]]], :ppath nil, :r ([3] [4])}]"

read it back:

user> (def deserialized-s (read-string "[[1 [2]] {:l [], :pnodes [[[1 [2]] [3] [4]]], :ppath nil, :r ([3] [4])}]"))
#'user/deserialized-s

do something with the result:

user> (zip/root deserialized-s)
[[1 [2]] [3] [4]]
Arthur Ulfeldt
  • 90,827
  • 27
  • 201
  • 284
  • Whoa, that is very cool. And I was thinking it was creating a closure all the way back up the path. Haha. My only remaining question then is why does print-str serialize a expression, while read-string only deserializes the first item from a string? It just seems a bit odd that they don't perfectly "match". – Stephen Cagle Apr 30 '12 at 22:59
  • 1
    The `print` family of functions are for human consumption, not for serialization. For example, strings and symbols print the same, so information is lost. Instead, use `pr-str`, which prints to a computer-friendly format (and is more or less the inverse of `read-string`). – amalloy May 01 '12 at 05:03
  • @StephenCagle If you have another question that is basically unrelated to this one, I recommend you ask it as a question, instead of as a comment on one of the answers here. But in short, there is no inconsistency: `print-string` prints one value to a string, and `read-string` reads one value from a string. – amalloy May 01 '12 at 06:18
  • @amalloy, fixed :) fortunately in this case the output it the same – Arthur Ulfeldt May 01 '12 at 17:42
  • just one note: As I understood zippers, they actually represent state of a subterm plus properly typed navigation operations. – Bastl Sep 10 '13 at 12:22
3

To expand on Arthur's answer:

You probably also need to serialize the metadata that clojure.zip puts on its zippers, since that seems to be where it keeps track of the branch?/children/make-node functions. So something like

(defn serialize [zipper]
  (binding [*print-meta* true]
    (pr-str zipper)))

(def deserialize read-string)
amalloy
  • 89,153
  • 8
  • 140
  • 205