Transforming the data after it comes out of the parser can be
cumbersome with complex data structures though, it would be nice to
have a way for the parser to return the data in the desired format.

I updated clojure.contrib.json with the ability to add custom
deserializers:

(def *deserializers* (atom {}))

(defn add-deserializer [k deserializer]
  (swap! *deserializers* #(assoc % k deserializer)))

(defn remove-deserializer [k]
  (swap! *deserializers* #(dissoc % k)))


(defn- read-json-object [#^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)")))

                     (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)))))))))))


all I had to change was the part where the key and value are put in
the map to first check if there is a deserializer registered and use
it instead of storing the data directly, then I can use it as follows

(defn write-date [date #^PrintWriter out]
  (.print out
    (str "\"" (.format (new java.text.SimpleDateFormat "MMM dd, yyyy
hh:mm:ss a") date) "\"")))

(extend Date Write-JSON
  {:write-json write-date})

(add-deserializer :age #(.parse (new java.text.SimpleDateFormat "MMM
dd, yyyy hh:mm:ss a") %))

(let [data {:name "John" :age (new java.util.Date) :address [:street
"1 Bay" :city "Toronto"]}
      encoded (json-str data)]
  (println encoded)
  (println "decoded" (read-json encoded)))


Would there be an issue with adding something like that to the
contrib?



On Aug 21, 1:52 pm, Stuart Sierra <the.stuart.sie...@gmail.com> wrote:
> I suppose one could override the (private) read-json-object function
> to transform maps after they are read, based on the presence of
> certain keys.  But that would seriously complicate the reader.  It's
> probably easier to transform the data after it comes back from the
> JSON parser.
>
> -S
>
> On Aug 20, 5:06 pm, Dmitri <dmitri.sotni...@gmail.com> wrote:
>
>
>
>
>
>
>
> > My concern is more to do with the reader, I think extending writer
> > works quite well, it would be nice if it was possible to do the same
> > thing with the reader, so you could specify how to deserialize
> > specific types of data. Right now it seems to be baked into read-json-
> > reader and there's no easy way to extend it.
>
> > Maybe it could be possible to specify how to deserialize data based on
> > the key names, then if the reader hits a key with the given name it
> > would try to deserialize it with the given function or something?
>
> > On Aug 20, 3:32 pm, Stuart Sierra <the.stuart.sie...@gmail.com> wrote:
>
> > > Since there is no standard for how to represent dates in JSON, it is
> > > unlikely to be built in.  But you can extend the writer with
> > > application-specific date formats.
> > > -S
>
> > > On Aug 20, 2:15 pm, Dmitri <dmitri.sotni...@gmail.com> wrote:
>
> > > > I'm currently using Dan Larkin's clojure-json, and it provides a way
> > > > to serialize and deserialize dates, it also provides the option to
> > > > specify custom serializers, eg:
>
> > > > (defn date-encoder
> > > >         [date writer pad current-indent start-token-indent indent-
> > > > size]
> > > >         (.append writer (str start-token-indent \" date \")))
>
> > > > I was looking at switching to using the json implementation in clojure-
> > > > contrib, but noticed that it doesn't handle dates nor does it provide
> > > > a way to register custom serializers, is there a plan to implement
> > > > that in the future, or is the proper approach to simply extend Write-
> > > > JSON whenever a custom serializer/deserializer is needed.

-- 
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

Reply via email to