I added the *deserializers* atom, converted read-json-object to a macro (def *deserializers* (atom {}))
(defn add-deserializer [k deserializer] (swap! *deserializers* #(assoc % k deserializer))) (defn remove-deserializer [k] (swap! *deserializers* #(dissoc % k))) (defmacro get-object-reader [] (let [store '(recur (.read stream) nil (assoc! result (if keywordize? (keyword key) key) element)) deserialize-store '(let [elm-key (if keywordize? (keyword key) key) deserializer (get @*deserializers* elm-key)] (recur (.read stream) nil (assoc! result elm-key (if deserializer (deserializer element) element)))) store-and-recur (if (empty? @*deserializers*) deserialize- store store)] `(fn [#^PushbackReader ~'stream ~'keywordize?] ;; Expects to be called with the head of the stream AFTER the ;; opening bracket. (loop [~'i (.read ~'stream), ~'key nil, ~'result (transient {})] (let [~'c (char ~'i)] (cond (= ~'i -1) (throw (EOFException. "JSON error (end-of- file inside object)")) (Character/isWhitespace ~'c) (recur (.read ~'stream) ~'key ~'result) (= ~'c \,) (recur (.read ~'stream) nil ~'result) (= ~'c \:) (recur (.read ~'stream) ~'key ~'result) (= ~'c \}) (if (nil? ~'key) (persistent! ~'result) (throw (Exception. "JSON error (key missing value in object)"))) :else (do (.unread ~'stream ~'i) (let [~'element (read-json-reader ~'stream ~'keywordize? true nil)] (if (nil? ~'key) (if (string? ~'element) (recur (.read ~'stream) ~'element ~'result) (throw (Exception. "JSON error (non-string key in object)"))) ~store-and-recur))))))))) and changed read-json-reader to use get-object-reader at the start of the loop (defn- read-json-reader ([#^PushbackReader stream keywordize? eof-error? eof-value] (loop [i (.read stream) object-reader (get-object-reader)] (let [c (char i)] (cond ;; Handle end-of-stream (= i -1) (if eof-error? (throw (EOFException. "JSON error (end-of- file)")) eof-value) ;; Ignore whitespace (Character/isWhitespace c) (recur (.read stream) object- reader) these are the only changes that are needed and should preserve the default case, while allowing to extend object reader with custom deserializers, would this solution be acceptable? On Aug 24, 8:21 pm, Dmitri <dmitri.sotni...@gmail.com> wrote: > I understand the desire to keep the parser clean, but at the same time > the ability to register custom data deserializers would be very > convenient. Would something like the following help with the > performance issue, since if no deserializers were registered there > would only be a one time penalty for selecting the object reader? > > (defn- read-json-reader > ([#^PushbackReader stream keywordize? eof-error? eof-value] > (loop [i (.read stream) > object-reader (if (empty? @*deserializers*) read-json- > object read-json-object-and-deserialize)] > > On Aug 24, 6:51 pm, Stuart Sierra <the.stuart.sie...@gmail.com> wrote: > > > > > On Aug 23, 9:03 pm, Dmitri <dmitri.sotni...@gmail.com> wrote: > > > > Would there be an issue with adding something like that to the > > > contrib? > > > I don't want to add anything that impacts performance in the plain > > parsing case. > > > -S -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en