unbean

48 views
Skip to first unread message

.Bill Smith

unread,
Feb 24, 2009, 10:30:47 PM2/24/09
to Clojure
I finally got around to writing an "unbean" function. As the name
suggests, it's the reverse of the bean function: it takes a class and
a map of property name/values and returns an instance of that class
with those property values. So for example, if class House has
properties "address", "color", and "area", you can do this:

user=> (def h (unbean House {:address "100 Elm Street" :color
"red" :area 2500}))
#'user/h
user=> (.getAddress h)
"100 Elm Street"
user=> (bean h)
{:address "100 Elm Street", :color "red", :area 2500}
user=>

Questions:
1. It's a real function, not a macro, and it uses reflection, so it's
not something you would want to use in a tight loop. Would a macro be
more appropriate?
2. Would this kind of thing be a useful addition to the core API?

Here's the code:

(defn unbean [clazz props]
(let [x (.newInstance clazz)
pmap (reduce (fn [m pd]
(let [name (.getName pd)
method (.getWriteMethod pd)]
(if (and method (= 1 (alength (. method (getParameterTypes)))))
(assoc m (keyword name) (fn [v] (. method (invoke x (into-array
[v])))))
m)))
{}
(.getPropertyDescriptors (java.beans.Introspector/getBeanInfo
clazz)))]
(doseq [kv props]
(((keyword (first kv)) pmap) (second kv)))
x))

Bill Smith


Michel S.

unread,
Feb 24, 2009, 11:46:33 PM2/24/09
to Clojure
On Feb 24, 10:30 pm, ".Bill Smith" <william.m.sm...@gmail.com> wrote:
> I finally got around to writing an "unbean" function.   As the name
> suggests, it's the reverse of the bean function: it takes a class and
> a map of property name/values and returns an instance of that class
> with those property values.  So for example, if class House has
> properties "address", "color", and "area", you can do this:
>
> user=> (def h (unbean House {:address "100 Elm Street" :color
> "red" :area 2500}))
> #'user/h
> user=> (.getAddress h)
> "100 Elm Street"
> user=> (bean h)
> {:address "100 Elm Street", :color "red", :area 2500}
> user=>
>

I tend to associate "bean" with Java beans, so the naming seems to be
reversed IMHO: "bean" should convert a Clojure map to a Java bean, and
"unbean" should do the reverse.

In Scheme-speak they would be map->bean and bean->map, which might be
a bit verbose.

It's getting late here so I don't have time to test, but would a
recursive map be converted to a recursive bean (i.e. some elements are
themselves beans) and vice versa with your functions?

Regards,

--
Michel

.Bill Smith

unread,
Feb 25, 2009, 12:18:31 AM2/25/09
to Clojure
> I tend to associate "bean" with Java beans, so the naming seems to be
> reversed IMHO: "bean" should convert a Clojure map to a Java bean, and
> "unbean" should do the reverse.

Agreed the name is awkward.

> It's getting late here so I don't have time to test, but would a
> recursive map be converted to a recursive bean (i.e. some elements are
> themselves beans) and vice versa with your functions?

The map alone is not sufficient to describe the object; you need the
class too. That's true both for the bean and any of it's bean-typed
properties, since a property might be typed with an interface or an
abstract class for which there is no constructor. To specify the
value of a bean-typed property, you'd do something like this:

(unbean House {:address "100 Elm Street" :architecture (unbean
Architecture {:style "Tudor Revival" :architect "F L Wright" :material
"concrete"})})

Of course the bean function doesn't walk the object hierarchy either.

Thanks for the feedback.
Bill

Kevin Albrecht

unread,
Feb 25, 2009, 1:24:51 PM2/25/09
to Clojure
It occurs to me that the "unbean" function could be very useful when
writing tests for code that calls Java objects. Anyone have thoughts
on its use in this way?

.Bill Smith

unread,
Feb 25, 2009, 2:39:44 PM2/25/09
to Clojure
> It occurs to me that the "unbean" function could be very useful when
> writing tests for code that calls Java objects.

Yes, that is exactly the use I have in mind.

Bill

Kevin Albrecht

unread,
Feb 26, 2009, 5:57:16 PM2/26/09
to Clojure
Thanks. I will definitely be using this function... keep me up to
date on any changes.

John D. Hume

unread,
Feb 27, 2009, 8:56:56 AM2/27/09
to clo...@googlegroups.com
On Wed, Feb 25, 2009 at 12:18 AM, .Bill Smith <william...@gmail.com> wrote:
> The map alone is not sufficient to describe the object; you need the
> class too.  That's true both for the bean and any of it's bean-typed
> properties, since a property might be typed with an interface or an
> abstract class for which there is no constructor.

You could gen-class as needed to create concrete interface
implementations or subclasses. This would probably be a terrible idea
if unbean were for general use, but if you're focused on testing it
could keep things cleaner (and prevent you cluttering up tests with
irrelevant details).

I suppose the most communicative approach would be to nest unbean
calls, but make unbean support abstract classes and interfaces as its
first argument. (If unbean inferred the need to gen-class, you
couldn't tell from reading the test code whether a property of the
top-level bean were a PersistentHashMap or some non-Clojure type being
figured out behind the scenes.)

-hume.

--
http://elhumidor.blogspot.com/

Reply all
Reply to author
Forward
0 new messages