Jay Fields is a software developer at DRW Trading. He has a passion for discovering and maturing innovative solutions. His most recent work has been in the Domain Specific Language space where he's delivered applications that empowered subject matter experts to write the business rules of the applications. He is also very interested in maturing software design through software testing. Jay is a DZone MVB and is not an employee of DZone and has posted 116 posts at DZone. You can read more from them at their website. View Full User Profile

Clojure: Converting a Java Object to a Clojure Map

  • submit to reddit

Clojure has a great library that helps facilitate most things you'd want to do, but it's also easy to roll your own solution if you need something more specific. This blog entry is about converting an object to a map, something Clojure already has a function for: bean. From the docs:

Takes a Java object and returns a read-only implementation of the
map abstraction based upon its JavaBean properties.
The bean function is easy enough to use, as the following example shows (formatted for readability).
user=> (import 'java.util.Calendar)
user=> (-> (Calendar/getInstance) .getTime bean)
{:seconds 16, 
 :date 2, 
 :class java.util.Date, 
 :minutes 35, 
 :hours 12, 
 :year 111, 
 :timezoneOffset 240, 
 :month 4, 
 :day 1, 
 :time 1304354116038}
The bean function is simple and solves 99% of the problems I encounter. Generally I'm destructuring a value that I pull from a map that was just created from an object; bean works perfectly for this.

However, I have run across two cases where I needed something more specialized.

The first case involved processing a large amount of data in a very timely fashion. We found that garbage collection pauses were slowing us down too much, and beaning the objects that were coming in was causing a large portion of the garbage. Since we were only dealing with one type of object we ended up writing something specific that only grabbed the fields we cared about.

The second case involved working with less friendly Java objects that needed to be converted to maps that were going to be used many times throughout the system. In general it isn't a big deal working with ugly Java objects. You can destructure the map into nice var names, or you can use the rename-keys function if you plan on using the map more than once. This probably would have worked for me, but I had an additional requirement. The particular Java objects I was working with had many methods/fields and the maps contained about 80% more information than I needed. So, I could have used bean, then rename keys, then dissoc. But, this felt similar enough to the situation in the past where I was working with specific method names and I thought I'd look for a more general solution.

The following example shows the definition of obj->map and the REPL output of using it.
user=> (import 'java.util.Date)
user=> (defmacro obj->map [o & bindings]
  (let [s (gensym "local")]
    `(let [~s ~o]
      ~(->> (partition 2 bindings)
        (map (fn [[k v]]
          (if (vector? v)
            [k (list (last v) (list (first v) s))]
            [k (list v s)])))
        (into {})))))
user=> (obj->map (Date. 2012 1 31)
    :month .getMonth
    :year [.getYear #(* 2 %)]
    :day .getDate)
{:month 2, :year 4024, :day 2}
As you can see from the output, the macro grabs the values of the methods you specify and stores them in a map. At one point I also needed to do a bit of massaging of a value, so I included the ability to use a vector to specify the method you care about and a function to apply to the value (the result of the function will become the value). In the example, you can see that the :year is double the value of what .getYear returns.

In general you should stick with bean, but if you need something more discriminating, obj->map might work for you.


From http://blog.jayfields.com/2011/05/clojure-converting-java-object-to.html

Published at DZone with permission of Jay Fields, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)


Carlo Sciolla replied on Thu, 2011/05/12 - 6:50am

Thanks for the interesting reading! Just one question, wouldn't it be better to remove the explicit call to gensym and do something like: (defmacro zxc [o] `(let [s# o] ...)) ?

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.