Use Java object as Clojure map

The function bean came to mind:

Takes a Java object and returns a read-only implementation of the map abstraction based upon its JavaBean properties.

Example taken from the site:

user=> (import java.util.Date)
java.util.Date

user=> (def *now* (Date.))
#'user/*now*

user=> (bean *now*)
{:seconds 57, :date 13, :class java.util.Date,
 :minutes 55, :hours 17, :year 110, :timezoneOffset -330,
 :month 6, :day 2, :time 1279023957492}

Clojure keywords can look up stuff in anything that implements the required (read-only) parts of the java.lang.Map interface. The problem is probably going to be that you're not actually using clojure keywords as keys so that might not help you.

As for IPersistentMap; your parser presumably doesn't implement anything relevant to the that interface.

Personally, I'd write a straight up conversion function. Clojure uses a lot of those (seq, for instance) and after converting, you know you're dealing with a real persistent map and not something that only acts like it some of the time (so you can actually call seq, keys, vals etc on it).

Alternatively;

  • just implement clojure.lang.ILookup, and leave out everything else.
  • convert using some generated/reflection code if you want something more generic. See https://github.com/joodie/clj-java-fields for an example.

Sure the (bean javaObject) (see bean ClojureDoc) works well, but it doesn't let you select the property you want and those you doesn't. It has impact when you input the resulting map into the json-str function, in that case you can get an error saying : "Don't know how to write JSON of ..."

And I find that annoying when I deal with NoSQL DB (mongoDB, neo4j) that accepts essentially JSON (like the underlying of neocons).

So what's my solution?

(defmacro get-map-from-object-props [object & props]
  ;->> will eval and reorder the next list starting from the end
  (->> (identity props) ;identity is here to return the 'props' seq
       ;map each property with their name as key and the java object invocation as the value
       ;the ~@ is here to unsplice the few properties
       (map (fn [prop] [(keyword (str prop)) `(.. ~object ~@(prop-symbol prop) )]))
       (into {})))

;getter is a simple function that transform a property name to its getter "name" -> "getName"
(defn prop-symbol [prop]
  (map symbol (map getter (clojure.string/split (str prop) #"\\."))))

And you can use it like that (yes the function takes care of a chain of property if any)

(get-map-from-object-props javaObject property1 property2 property3.property1)

Hope that will help someone...