Recursively convert Java Map to Clojure Map

1,294 views
Skip to first unread message

Jestan Nirojan

unread,
Oct 15, 2011, 4:21:18 AM10/15/11
to Clojure
Hi all,

I have a Java Map contains Map of Maps/List (JSON like map) and have
to convert to Clojure map (This happens at Java - Clojure Interop), So
I have written a converter function 'as-clj-map' by modifying the
clojure walk functions, It works fine, but consume lot of cpu when the
data structure is quit big. Any suggestions to improve this code?


(defn walk-coll [inner outer form]
(cond
(instance? java.util.List form) (outer (into [] (map inner
form)))
(instance? java.util.Map form) (outer (into {} (map inner
form)))
:else (outer form)))

(defn postwalk-coll [f form]
(walk-coll (partial postwalk-coll f) f form))

(defn as-clj-map [m]
(let [f (fn [[k v]] (if (string? k) [(keyword k) v] [k v]))]
(postwalk-coll (fn [x] (if (instance? java.util.Map x) (into {}
(map f x)) x)) m)))


Thanks and regards.
-Jestan Nirojan

Baishampayan Ghose

unread,
Oct 15, 2011, 5:10:32 AM10/15/11
to clo...@googlegroups.com
> I have a Java Map contains Map of Maps/List (JSON like map) and have
> to convert to Clojure map (This happens at Java - Clojure Interop), So
> I have written a converter function 'as-clj-map' by modifying the
> clojure walk functions, It works fine, but consume lot of cpu when the
> data structure is quit big. Any suggestions to improve this code?

What about using protocols for this job?

(defprotocol ConvertibleToClojure
(->clj [o]))

(extend-protocol ConvertibleToClojure
java.util.Map
(->clj [o] (let [entries (.entrySet o)]
(reduce (fn [m [^String k v]]
(assoc m (keyword k) (->clj v)))
{} entries)))

java.util.List
(->clj [o] (vec (map ->clj o)))

java.lang.Object
(->clj [o] o)

nil
(->clj [_] nil))

(defn as-clj-map
[m]
(->clj m))


Let me know if this works for you & meets your performance requirements.

Regards,
BG

--
Baishampayan Ghose
b.ghose at gmail.com

Jestan Nirojan

unread,
Oct 16, 2011, 1:46:51 AM10/16/11
to Clojure
Your solution worked, about 4x~5x improvement.
Thanks a lot BG.

regards.
-Jestan Nirojan

Baishampayan Ghose

unread,
Oct 16, 2011, 1:51:14 AM10/16/11
to clo...@googlegroups.com
On Sun, Oct 16, 2011 at 11:16 AM, Jestan Nirojan
<jestan...@gmail.com> wrote:
> Your solution worked, about 4x~5x improvement.
> Thanks a lot BG.

Glad that it worked for you, Jestan. Protocols are usually the right
approach for these kind of tasks.

Chouser

unread,
Oct 16, 2011, 1:46:25 PM10/16/11
to clo...@googlegroups.com
On Sun, Oct 16, 2011 at 1:51 AM, Baishampayan Ghose <b.g...@gmail.com> wrote:
> On Sun, Oct 16, 2011 at 11:16 AM, Jestan Nirojan
> <jestan...@gmail.com> wrote:
>> Your solution worked, about 4x~5x improvement.
>> Thanks a lot BG.
>
> Glad that it worked for you, Jestan. Protocols are usually the right
> approach for these kind of tasks.

Why convert at all? Java maps can be used quite conveniently in Clojure.

If it's because you need the map to be persistent, you might consider
converting each map only when the user attempts to conj or assoc,
rather than doing all the work up front. Since those aren't (yet!)
protocols, I supposed you'd have to either use a non-standard conj and
assoc, or create a wrapper around the Java Maps. Of course there are
complications with all three of these solutions, so you'll have to
choose carefully based on your needs.

--Chouser

Jason Ross

unread,
Jan 31, 2020, 4:07:24 PM1/31/20
to Clojure
Hey I know this is super old post but what would the reverse look like, eg. recursively convert Clojure to java map

Alex Miller

unread,
Jan 31, 2020, 4:15:29 PM1/31/20
to Clojure
You don't need that - Clojure maps *are* Java maps (they implement java.util.Map) and you can pass them into most Java APIs as is (with the caveat that they are made for reading, not for writing).

If you did really want to convert them to hash-maps or whatever, it's pretty easy to do so with a clojure.walk/postwalk-replace.

Jason Ross

unread,
Jan 31, 2020, 4:20:38 PM1/31/20
to Clojure
Thanks for the reply. I do need to convert to java hashmaps and arraylists because I'm trying to duplicate the testing of a clojure workflow being run on a server thats pushing pure java context through it. So in my tests I define a clojure map but want to javafy it to force errors to happen when I try to evaluate the map as a function. 

It's not clear to my how the postwalk-replace will work for converting specific types (clojure maps and lists) to other types (java hashmaps and array lists) recursively. Is the extend-protocol method discussed below not the way to go?
Reply all
Reply to author
Forward
0 new messages