views:

4252

answers:

9

Say I have a very simple java object that only has some getXXX and setXXX properties. This object is used only to handle values, basically a record or a type-safe (and performant) map. I often need to covert this object to key value pairs (either strings or type safe) or convert from key value pairs to this object.

Other than reflection or manually writing code to do this conversion, what is the best way to achieve this?

An example might be sending this object over jms, without using the ObjectMessage type (or converting an incoming message to the right kind of object).

+6  A: 

Code generation would be the only other way I can think of. Personally, I'd got with a generally reusable reflection solution (unless that part of the code is absolutely performance-critical). Using JMS sounds like overkill (additional dependency, and that's not even what it's meant for). Besides, it probably uses reflection as well under the hood.

Michael Borgwardt
Performance is critical so I think reflection is out. I was hoping that there might be some tools that use asm or cglib. I have started looking at Google's protocol buffers again. I didn't get your comment about JMS, I am using it to communicate information across machines.
Shahbaz
I misunderstood that. As for performance, there's a difference between performance being important and that specific part being performance-critical. I'd do a benchmark to see how much it really matters compared to what else the application is doing.
Michael Borgwardt
That's also my thought. Either you use reflection (directly or indirectly) or you have to generate code. (Or write the extra code yourself, of course).
extraneon
+11  A: 

There is always apache commons beanutils but of course it uses reflection under the hood

Maurice Perry
Besides, there is nothing wrong about reflection being used, under the hood. Especially if results are cached (for future use), reflection-based access is usually fast enough for most use cases.
StaxMan
+1  A: 

JSON, for example using XStream + Jettison, is a simple text format with key value pairs. It is supported for example by the Apache ActiveMQ JMS message broker for Java object exchange with other platforms / languages.

mjustin
+1  A: 

You could use the Joda framework:

http://joda.sourceforge.net/

and take advantage of JodaProperties. This does stipulate that you create beans in a particular way however, and implement a specific interface. It does then however, allow you to return a property map from a specific class, without reflection. Sample code is here:

http://pbin.oogly.co.uk/listings/viewlistingdetail/0e78eb6c76d071b4e22bbcac748c57

Jon
Looks interesting, unfortunately it doesn't look like it is maintained any more. Besides, the property map it returns is a map of <String,String>, so not type safe.
Shahbaz
True, it hasn't been maintained since 2002. I was just curious as to whether a non-reflection based solution existed. The property map it returns is actually just a standard map, no generics as such...
Jon
A: 

My JavaDude Bean Annotation Processor generates code to do this.

http://javadude.googlecode.com

For example:

@Bean(
  createPropertyMap=true,
  properties={
    @Property(name="name"),
    @Property(name="phone", bound=true),
    @Property(name="friend", type=Person.class, kind=PropertyKind.LIST)
  }
)
public class Person extends PersonGen {}

The above generates superclass PersonGen that includes a createPropertyMap() method that generates a Map for all properties defined using @Bean.

(Note that I'm changing the API slightly for the next version -- the annotation attribute will be defineCreatePropertyMap=true)

Scott Stanchfield
Have you done code-reviews? This doensn't seem to be a good approach! You change the inheritance path with annotations! What if the bean extends already at a class?! Why have you to write the attributes twice?
Martin K.
If you already have a superclass, you use @Bean(superclass=XXX.class, ...) and it inserts the generated superclass inbetween. This has been great for code reviews -- much less potentially-error-prone boilerplate to sift though.
Scott Stanchfield
Not sure what you mean by "write the attributes twice" -- where do they show up twice?
Scott Stanchfield
@Martin K. If you really don't want to use reflection, even under the hood, then you're probably forced to do some kind of code generation.
extraneon
A: 

If you do not want to hardcode calls to each getter and setter, reflection is the only way to call these methods (but it is not hard).

Can you refactor the class in question to use a Properties object to hold the actual data, and let each getter and setter just call get/set on it? Then you have a structure well suited for what you want to do. There is even methods to save and load them in the key-value form.

Thorbjørn Ravn Andersen
There are no methods, because it depends on you what is a key and what is value! It should be easy to write a service which does such a conversion. For a simple representation CSV might be acceptable.
Martin K.
Sorry, your answer doesn't make sense to me...
Thorbjørn Ravn Andersen
A: 

You should write a generic transformation Service! Use generics to keep it type free (so you can convert every object to key=>value and back).

What field should be the key? Get that field from the bean and append any other non transient value in a value map.

The way back is pretty easy. Read key(x) and write at first the key and then every list entry back to a new object.

You can get the property names of a bean with the apache commons beanutils!

Martin K.
A: 

The best solution is to use Dozer. You just need something like this in the mapper file:

<mapping map-id="myTestMapping">
  <class-a>org.dozer.vo.map.SomeComplexType</class-a>
  <class-b>java.util.Map</class-b>
</mapping>

And that's it, Dozer takes care of the rest!!!

Dozer Documentation URL

Why does it require mapping file? If it knows how to convert, why require this extra work -- just take source object, expected type?
StaxMan
Dozer uses reflection (FAQ question 1) which is deemed to be to slow...
extraneon
Ok. It's good to know the reason, even though I'm not sure it's valid :)
StaxMan
+1  A: 

Lots of potential solutions, but let's add just one more. Use Jackson (JSON processing lib) to do "json-less" conversion, like:

ObjectMapper m = new ObjectMapper(); Map props = m.convertValue(myBean, Map.class); MyBean anotherBean = m.convertValue(props, MyBean.class);

(this blog entry has some more examples)

You can basically convert any compatible types: compatible meaning that if you did convert from type to JSON, and from that JSON to result type, entries would match (if configured properly can also just ignore unrecognized ones).

Works well for cases one would expect, including Maps, Lists, arrays, primitives, bean-like POJOs.

StaxMan