The map data structure is used a lot in Clojure. When we want to use Java objects in our Clojure code we can convert the Java object to a map with the bean function. This function will use reflection to get all the properties of the Java object and converts each property with the property value to a key with value in the resulting map. The bean function will not recursively convert nested objects to a map, only the top-level properties are turned into key value pairs.

We see several examples of using the bean function in the following code snippet:

(ns mrhaki.core.bean
  (:require [clojure.test :refer [is]])
  (:import ( URI)
           ( Person Address)))

(is (= {:path "",
        :rawQuery "search=clojure",
        :fragment nil,
        :authority "",
        :rawAuthority "",
        :port -1,
        :absolute true,
        :host "",
        :rawPath "",
        :opaque false,
        :rawSchemeSpecificPart "//",
        :rawUserInfo nil,
        :query "search=clojure",
        :rawFragment nil,
        :scheme "https",
        :userInfo nil,
        :schemeSpecificPart "//"}
       (bean (URI. ""))))

(comment "For the next sample we use a Java class Person
  with properties name and alias of type String and
  a third property of type Address. The Java class Address
  has a single String property city.

  Pseudo code:
  class Person { String name, alias; Address address; }
  class Address { String city; }")

(def person (Person. "Hubert" "mrhaki" (Address. "Tilburg")))

(is (= {:name "Hubert" :alias "mrhaki"}
       (select-keys (bean person) [:name :alias])))

;; Properties with custom classes are not automatically
;; also converted to a map representation.
(is (instance? (:address (bean person))))

(is (= {:city "Tilburg"}
       (select-keys (bean (:address (bean person))) [:city])))

(is (= {:address {:city "Tilburg", :class},
        :alias "mrhaki",
        :name "Hubert"}
       (assoc (bean person) :address (bean (.getAddress person)))))

Written with Clojure 1.10.1.