Didac Montero
Didac Montero

Reputation: 2086

reflection copy non null properties from one object to another BeanUtils

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?

Upvotes: 2

Views: 4804

Answers (1)

Syam S
Syam S

Reputation: 8499

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();
        }
    }
    }
}

Upvotes: 5

Related Questions