Search code examples
javaspringjavabeans

Copy non-null properties from one object to another using BeanUtils or similar


my aim is to copy fields of one object into another, but only those that aren't null. I don't want to assign it explicitly. A more generic solution would be very useful and easier to maintain i.e. for implementing PATCH in REST API where you allow providing only specific fields.

I saw this similar thread and I'm trying to implement some of the ideas from here: Helper in order to copy non null properties from object to another ? (Java)

But the objects aren't altered in any way after the program execution.

So here are my example classes created for example:

class Person {
    String name;
    int age;
    Pet friend;

    public Person() {
    }

    public Person(String name, int age, Pet friend) {
        this.name = name;
        this.age = age;
        this.friend = friend;
    }

    // getters and setters here
}

class Pet {
    String name;
    int age;

    public Pet(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // getters and setters here
}

Here is my overridden copyProperty method:

import org.apache.commons.beanutils.BeanUtilsBean;
import java.lang.reflect.InvocationTargetException;

public class MyBeansUtil extends BeanUtilsBean {

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

... and here is the place I'm trying to test it on some examples:

public class SandBox {
    public static void main(String[] args) {
        Person db = new Person("John", 36, new Pet("Lucy", 3));
        Person db2 = new Person("John", 36, new Pet("Lucy", 2));
        Person db3 = new Person("John", 36, new Pet("Lucy", 4));

        Person in = new Person();
        in.age = 17;
        in.name = "Paul";
        in.friend = new Pet(null, 35);

        Person in2 = new Person();
        in2.name = "Damian";

        Person in3 = new Person();
        in3.friend = new Pet("Lup", 25);

        try {
            BeanUtilsBean notNull  =new MyBeansUtil();
            notNull.copyProperties(db, in);
            notNull.copyProperties(db2, in2);
            notNull.copyProperties(db3, in3);

        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}

Unfortunately, the original objects db, db1, db2 stay the same as they were. Am I doing something wrong here?


Solution

  • I ended up using Spring BeanUtils library. Here is my working method:

    import org.springframework.beans.BeanWrapper;
    import org.springframework.beans.BeanWrapperImpl;
    
    import java.lang.reflect.Field;
    import java.util.Collection;
    
    public class MyBeansUtil<T> {
        public T copyNonNullProperties(T target, T in) {
            if (in == null || target == null || target.getClass() != in.getClass()) return null;
    
            final BeanWrapper src = new BeanWrapperImpl(in);
            final BeanWrapper trg = new BeanWrapperImpl(target);
    
            for (final Field property : target.getClass().getDeclaredFields()) {
                Object providedObject = src.getPropertyValue(property.getName());
                if (providedObject != null && !(providedObject instanceof Collection<?>)) {
                    trg.setPropertyValue(
                            property.getName(),
                            providedObject);
                }
            }
            return target;
        }
    }
    

    It works fine, but notice that it ignores fields that are collections. That's on purpose, I handle them separately.