I need to save clojure maps to a file and read them back later to process them.
This is what I could come up with. Is there a better way to accomplish the same thing?
It is easiest to read a single form to and from the file, so I usually put my data into a vector.
I also prefer to use pr or pr-str
rather than print because it is guaranteed to produce readable data,
(def my-data [{:a "foo" :b [1 2 3]} "asdf" 42 #{1 2 3}])
(spit "/tmp/data.edn" (with-out-str (pr my-data)))
nil
(read-string (slurp "/tmp/data.edn"))
[{:a "foo", :b [1 2 3]} "asdf" 42 #{1 2 3}]
vs:
(spit "/tmp/data.edn" (with-out-str (print my-data)))
(read-string (slurp "/tmp/data.edn"))
[{:a foo, :b [1 2 3]} asdf 42 #{1 2 3}]
notice how the string `"asdf" was read back as a symbol.
.toString
also works fine:
(spit "/tmp/data.edn" (.toString my-data))
(read-string (slurp "/tmp/data.edn"))
[{:a "foo", :b [1 2 3]} "asdf" 42 #{1 2 3}]
Yes - spit
and prn-str
for writing, slurp
and read-string
for reading.
user=> (def a [1 2 3 4])
#'user/a
user=> (prn-str a)
"[1 2 3 4]\n"
user=> (spit "stored-array.dat" (prn-str a))
nil
(in a new REPL session)
user=> (require 'clojure.edn)
nil
user=> (slurp "stored-array.dat")
"[1 2 3 4]\n"
user=> (clojure.edn/read-string (slurp "stored-array.dat"))
[1 2 3 4]
I used a vector for that example, but any data (e.g. maps) should work just as well.
Note that there is a read-string in the main Clojure language, but this is explicitly documented as not being safe for untrusted data, so it's better to use the version from clojure.edn.
Take a look at this question that deals with print-dup
and serializing forms. It has a better coverage of the trade-offs and security issues involved with each method.