clojure.core provides bean that creates a clojure map from a java object but is there an inverse function that creates a bean (java object) from a map?
2 Answers
Depends on what you need exactly.
Bean type exists in Java
In other words, you have (let's say) a Customer.class in your java project and you'd like to have that instantiated and filled in via Clojure based on the data in your map. This can be done simply via java interop.
Bean type created in Clojure
You can use AOT compilation to create a Javabean type, which will result in a .class file on the disk and can be used in a Java project (in the same way as any Java class, that does not have a corresponding Java source). You can use the clj-bean library to avoid using gen-class manually.
Bean type created in Clojure - dynamically, without .class file
In this case, you can use deftype to create the bean via dynamic bytecode creation (simply create methods for the getters and setters and move the data from-to the map).
You can find examples of all the above ways in this project
Comments
There are various libraries that perform Clojure map <-> Java Bean translation, including one I authored called bean-dip. Here's the breakdown in the README on the other options considered and what distinguishes bean-dip:
...
Existing translation solutions had feature gaps that lead us to create bean-dip: The
clojure.core/beanbuilt-in is one-way, uses uncached reflection and can't be configured. Cached reflection is available via gavagai, but it's only one-way. There's java.data, which is bidirectional, recursive and reflection-free, but it's not declarative making large translation layers hard to maintain.Bean-dip is:
- Bidirectional: Translate from beans to maps and back again
- Declarative: Just specify the bean class and a set of keys for corresponding maps
- Recursive: Translation descends into nested beans when they also have translations defined
- Reflection-free: Macro generates type hinted code that can be name checked at compile time via
*warn-on-reflection*- Extensible: Translate values by key via implementations of multimethods
Namespaced keys are supported making it easy to enforce contracts with Java APIs using specs...
{:foo "bar"}becomeso.foo == "bar"Objectbecausejava.lang.Objecthas no fields.