views:

64

answers:

4

Typically I could copy values between two java beans , which have identical property names, using beanutils with java reflection e.g. PropertyUtils.setProperty(....)

In protobuf Message, we use the message builder class to set the value. This works but I would rather use reflection to automatically copy properties from the bean to the message as both have identical property names and type.

When I invoke the PropertyUtils.setProperty on the builder object ( got from message.newBuilder() ) , I get this message.

java.lang.NoSuchMethodException: Property 'testProp' has no setter method in class 'class teststuff.TestBeanProtos$TestBeanMessage$Builder'

How do I automatically copy values from java bean to protobuf message object ( and vice-versa) using java reflection ?

A: 

You can go throw all properties getClass().getFields() and make copy using reflection. It will be smt like:

for(Field f : to.getClass().getFields()){
    f.set(to, from.getClass().getField(f.getName()).get(from));
}

+ probably you might be use field.setAccessible(true) invocation.

Stas
No , this could not be used in this context. The message builder is a sub class of the protobuf message object. and its setter methods does a lot of "housekeeping" like operations ; overriding its method accessibility setting would lead to problems in message serialization .
A: 

I hate to answer my question but I cant believe that I am the only one who ran into this problem. Documenting solution here in case other people are also getting started with protobuf and java. Using reflection saves wrting dozens of getter and setters.

Ok , I managed to get it to work using some of example test code shipping with protobuf. This is a very simple use case; typically a message would be a lot more complex. This code does not handle nested messages or repeated messages .

    public static void setMessageBuilder(com.google.protobuf.GeneratedMessage.Builder message,Descriptors.Descriptor descriptor,Object srcObject) throws Exception {


    String cname = srcObject.getClass().getName();
    /*BeanMapper.getSimpleProperties -- this is a warpper method that gets the list of property names*/ 
    List<String> simpleProps = BeanMapper.getSimpleProperties(srcObject.getClass());




    Map map = new HashMap();
    for (String pName : simpleProps) {
        System.out.println(" processing property "+ pName);
        Object value= PropertyUtils.getProperty(srcObject, pName);
        if(value==null) continue;

        Descriptors.FieldDescriptor fd=descriptor.findFieldByName(pName) ;

        System.out.println(" property "+  pName+" , found fd :"+ (fd==null ? "nul":"ok"));
         message.setField(fd, value);
         System.out.println(" property "+  pName+"  set ok,");

    }



    return ;
}
A: 

I may be off, but would protostuff help? It has nice extended support for working with other data formats, types. And even if it didn't have direct conversion support, if you go to/from JSON there are many choices for good data binding.

StaxMan
Thanks for a heads up on protostuf; it seems to be very powerful . I am still trying to play with protostuf' ProtostuffIOUtil. If I could get the message objects properly composed from the legacy bean, I would mark you response as the accepted answer.
A: 

I don't know the size of your project but you may want to try Dozer, a mapper that recursively copies data from one object to another of the same type or between different complex types. Supports implicit and explicit mapping as well. I used it in a big project and worked very well. It could be as simple as

Mapper mapper = new DozerBeanMapper();
DestinationObject destObject = mapper.map(sourceObject, DestinationObject.class);
Ither