Proper EDN serialization

This commit is contained in:
Jeremy Penner 2013-05-13 18:04:13 -04:00
parent ecc4fa4b7e
commit ab8a0c5860
2 changed files with 93 additions and 11 deletions

View file

@ -1,21 +1,23 @@
(ns hottub.serialize (ns hottub.serialize
(:require [clojure.edn :as edn])) (:require [clojure.edn :as edn]
[net.information-superhighway.edn :as ednw]))
(defmethod print-dup clojure.lang.PersistentQueue [o w] (defn is-queue [q] (isa? (type q) clojure.lang.PersistentQueue))
(.append w "#hottub/queue ") (defn serialize-queue [q] (into [] q))
(print-dup (into [] o) w)) (defn deserialize-queue [l] (into clojure.lang.PersistentQueue/EMPTY l))
(defn deserialize-queue [l] (def writer-opts
(into clojure.lang.PersistentQueue/EMPTY l)) {:tagged-serializers ['hottub.queue is-queue serialize-queue]})
(def reader-opts
{:readers {'hottub.queue deserialize-queue}})
(defn write (defn write
([o] (with-open [w (java.io.StringWriter.)] ([o] (ednw/stringify o writer-opts))
(write o w) ([o w] (ednw/write o w writer-opts)))
(.toString w)))
([o w] (print-dup o w)))
(defmulti read class) (defmulti read class)
(defmethod read String [s] (read (java.io.StringReader. s))) (defmethod read String [s] (read (java.io.StringReader. s)))
(defmethod read java.io.Reader [r] (defmethod read java.io.Reader [r]
(with-open [pbr (java.io.PushbackReader. r)] (with-open [pbr (java.io.PushbackReader. r)]
(edn/read {:readers {'hottub/queue deserialize-queue}} pbr))) (edn/read reader-opts pbr)))

View file

@ -0,0 +1,80 @@
(ns net.information-superhighway.edn)
(defn serialize-pr [val opts w]
"Serializes an arbitrary value to EDN using clojure.core/pr."
(binding [*out* w] (pr val)))
(defn serialize-number [num opts w]
"Serialize a number to EDN. Converts ratios to doubles."
(let [out (if (integer? num) num (double num))]
(.append w (str out))))
(declare write)
(defn- serialize-children [open close vals opts w]
(.append w open)
(loop [vals vals isfirst true]
(if (seq vals)
(do
(if-not isfirst (.append w " "))
(write (first vals) opts w)
(recur (next vals) false))))
(.append w close))
(defn serialize-list [l opts w]
(serialize-children "(" ")" l opts w))
(defn serialize-set [s opts w]
(serialize-children "#{" "}" s opts w))
(defn serialize-vector [v opts w]
(serialize-children "[" "]" v opts w))
(defn serialize-map [m opts w]
(serialize-children "{" "}" (mapcat identity m) opts w))
(def default-serializers
[keyword? serialize-pr
string? serialize-pr
number? serialize-number
#(or (= % true) (= % false)) serialize-pr
map? serialize-map
vector? serialize-vector
nil? serialize-pr
symbol? serialize-pr
list? serialize-list
set? serialize-set
char? serialize-pr])
(defn write-default [val opts w]
"Serialize to EDN using only the baseline serializers (no tagged values)"
(loop [serializers (partition 2 default-serializers)]
(if (seq serializers)
(let [[fntest fnserialize] (first serializers)]
(if (fntest val)
(fnserialize val opts w)
(recur (next serializers))))
(throw (Exception. (str "Could not serialize " val))))))
(defn serialize-tagged [tag value opts w]
(.append w (str "#" tag " "))
(write-default value opts w))
(def default-opts nil)
(defn write
([val w] (write val default-opts w))
([val opts w]
(loop [tagged-serializers (partition 3 (:tagged-serializers opts))]
(if (seq tagged-serializers)
(let [[tag fntest fnserialize] (first tagged-serializers)]
(if (fntest val)
(serialize-tagged tag (fnserialize val) opts w)
(recur (next tagged-serializers))))
(write-default val opts w)))))
(defn stringify
([val] (stringify val default-opts))
([val opts]
(with-open [w (java.io.StringWriter.)]
(write val opts w)
(.toString w))))