Search code examples
clojuresortedmapedn

Serializing sorted maps in Clojure / EDN?


How can I serialize and deserialize a sorted map in Clojure?

For example:

(sorted-map :a 1 :b 2 :c 3 :d 4 :e 5)
{:a 1, :b 2, :c 3, :d 4, :e 5}

What I've noticed:

  1. A sorted map is displayed in the same way as an unsorted map in the REPL. This seems convenient at times but inconvenient at others.
  2. EDN does not have support for sorted maps.
  3. Clojure does support custom tagged literals for the reader.

Additional resources:


Solution

  • Same question with two usable answers: Saving+reading sorted maps to a file in Clojure.

    A third answer would be to set up custom reader literals. You'd print sorted maps as something like

    ;; non-namespaced tags are meant to be reserved
    #my.ns/sorted-map {:foo 1 :bar 2}
    

    and then use an appropriate data function when reading (converting from a hash map to a sorted map). There's a choice to be made as to whether you wish to deal with custom comparators (which is a problem impossible to solve in general, but one can of course choose to deal with special cases).

    clojure.edn/read accepts an optional opts map which may contain a :reader key; the value at that key is then taken to be a map specifying which data readers to use for which tags. See (doc clojure.edn/read) for details.

    As for printing, you could install a custom method for print-method or use a custom function for printing your sorted maps. I'd probably go with the latter solution -- implementing built-in protocols / multimethods for built-in types is not a great idea in general, so even when it seems reasonable in a particular case it requires extra care etc.; simpler to use one's own function.

    Update:

    Demonstrating how to reuse IPersistentMap's print-method impl cleanly, as promised in a comment on David's answer:

    (def ^:private ipm-print-method
      (get (methods print-method) clojure.lang.IPersistentMap))
    
    (defmethod print-method clojure.lang.PersistentTreeMap
      [o ^java.io.Writer w]
      (.write w "#sorted/map ")
      (ipm-print-method o w))
    

    With this in place:

    user=> (sorted-map :foo 1 :bar 2)
    #sorted/map {:bar 2, :foo 1}