Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ClojureScript - convert arbitrary JavaScript object to Clojure Script map

I am trying to convert a Javascript object to a Clojure. However, I get the following error :

 (js/console.log (js->clj e)) ;; has no effect
 (pprint (js->clj e)) ;; No protocol method IWriter.-write defined for type object: [object Geoposition]

Yes, this object comes from the Geolocation API. I suppose that I have to extend IEncodeClojure and IWriter, but I have no clue how.

For instance adding the following :

(extend-protocol IEncodeClojure
  Coordinates
  (-js->clj [x options]
    (println "HERE " x options)))

Yields an error when loading my code : Uncaught TypeError: Cannot read property 'prototype' of undefined

like image 294
nha Avatar asked Sep 08 '15 21:09

nha


1 Answers

The accepted answer wasn't working for me with the javascript object window.performance.timing. This is because Object.keys() doesn't actually return the props for the PerformanceTiming object.

(.keys js/Object (.-timing (.-performance js/window))
; => #js[]

This is despite the fact that the props of PerformanceTiming are indeed iterable with a vanilla JavaScript loop:

for (a in window.performance.timing) {
  console.log(a);
}
// navigationStart
// unloadEventStart
// unloadEventEnd
// ...

The following is what I came up with to convert an arbitrary JavaScript object to a ClojureScript map. Note the use of two simple Google Closure functions.

  • goog.typeOf wraps typeof, which isn't normally accessible to us in ClojureScript. I use this to filter out props which are functions.
  • goog.object.getKeys wraps for (prop in obj) {...}, building up an array result which we can reduce into a map.

Solution (flat)

(defn obj->clj
  [obj]
  (-> (fn [result key]
        (let [v (goog.object/get obj key)]
          (if (= "function" (goog/typeOf v))
            result
            (assoc result key v))))
      (reduce {} (.getKeys goog/object obj))))

Solution (recursive)

Update: This solution will work for nested maps.

(defn obj->clj
  [obj]
  (if (goog.isObject obj)
    (-> (fn [result key]
          (let [v (goog.object/get obj key)]
            (if (= "function" (goog/typeOf v))
              result
              (assoc result key (obj->clj v)))))
        (reduce {} (.getKeys goog/object obj)))
    obj))
like image 112
Aaron Blenkush Avatar answered Sep 25 '22 22:09

Aaron Blenkush