2

Having this:

public class Parent {
    private String name;
    private int age;
    private Date birthDate;
    private Work work;
    static class Work{
        private int years;
        private String employer;
    }

// getters and setters   
    public static void main(String[] args) {
        Parent c = new Parent;
        c.setAge(55)
        Work work=new Parent.Work();
        work.setEmployer("Example");
        c.setWork(work);
        //save c in a DB...
    }
}

I want to copy only the no-null attributes using reflection. The approach described here with beanUtils works very well, but it copies all the no-null wrapped object, and not only the no-null field values:

//fetch c from the db...
Parent sameParent= new Parent;
sameParent.setWork(new Parent.Work());
//Directly from https://stackoverflow.com/questions/1301697/helper-in-order-to-copy-non-null-properties-from-object-to-another-java#answer-3521314
BeanUtilsBean notNull=new NullAwareBeanUtilsBean();
notNull.copyProperties(c, sameParent);

Now, Parent c will have the field age=55. The field work.employer will be null because the object Work has been overridden. Is it possible to modify the @Override copyProperty method from BeanUtilsBean to recursively copy only the desired (not null) attributes also from the wrapped objects?

Otherwise, do you know some other way?

Community
  • 1
  • 1
Didac Montero
  • 2,046
  • 19
  • 27
  • I also need to do similar stuff but, i have a doubt here although we copy those property alone which is having value as not null but, with the java reference type by default value is null. So copied bean will have null value for "name" and birthDate for you r example? so how we can avoid this? I am stuck here. – Gautam Jun 23 '14 at 17:02
  • Yes. In my example the rest of the field will have a null value. And that's expected because I didn't set any value in any moment. – Didac Montero Jun 24 '14 at 07:12

1 Answers1

5

You can achieve this by a simple tweaking in that NullAwareBeanUtilsBean. What i did here is to check for the type of the property to be copied and if it is not a primitive type then recursively call copyProperties method. Java auto-boxing converts the primitives to its wrapper classes so i'm using a set to identify the primitive types. Alternatively you could check for class types of packages to idetify custom objects and do recursive calls only for custom and avoid all java objects like String, Date etc. Also note this class does not handle primitive values. If age in target is set toa value and age in source is not initializes it'll still overwrite. Example if age in source was set to 20 and used didnt set the age for new object then copy will overwrite the value 20 to 0. You may need to put additional logic for that inside the if-primitive check.

import java.lang.reflect.InvocationTargetException;
import java.util.HashSet;
import java.util.Set;

import org.apache.commons.beanutils.BeanUtilsBean;

public class NullAwareBeanUtilsBean extends BeanUtilsBean {
    private static final Set<Class<?>> primitiveTypes = new HashSet<Class<?>>(
    Arrays.asList(Boolean.class, Character.class, Byte.class, Short.class, Short.class, Integer.class, Long.class, Float.class, Double.class, Void.class, String.class, Date.class));

    private static final Set<Class<?>> primitiveTypes = new HashSet<Class<?>>() {
    {
        add(Boolean.class);
        add(Character.class);
        add(Byte.class);
        add(Short.class);
        add(Integer.class);
        add(Long.class);
        add(Float.class);
        add(Double.class);
        add(Void.class);
    }
    };

    @Override
    public void copyProperty(Object dest, String name, Object value)
        throws IllegalAccessException, InvocationTargetException {
    if (value == null)
        return;

    if (primitiveTypes.contains(value.getClass())) {
        super.copyProperty(dest, name, value);
    } else {
        try {
        Object childObj = getPropertyUtils().getSimpleProperty(dest, name);
        if (childObj == null || orig instanceof List && !((List)orig).isEmpty() && ((List)orig).get(0).getClass().isEnum()) {
            childObj=orig;
        }else{
            copyProperties(childObj, orig);
        }
        super.copyProperty(dest, name, childObj);
        } catch (NoSuchMethodException e) {
        e.printStackTrace();
        }
    }
    }
}
Syam S
  • 8,421
  • 1
  • 26
  • 36
  • When the entities contains List, that approach doesn't work. The new lists will never be updated in the dest object, like explained here: http://stackoverflow.com/questions/19312055/beanutils-copyproperties-to-copy-arraylist – Didac Montero Jun 25 '14 at 11:37
  • 1
    when in `copyProperties(childObj, orig);` childObj is null, it throws an IllegalArgumentException: No spefici bean. I eddited your post to check for null – Didac Montero Jun 27 '14 at 14:13
  • copyProperties() is static method http://commons.apache.org/proper/commons-beanutils/javadocs/v1.9.3/apidocs/index.html – Sadanand Feb 27 '17 at 03:52