Helper in order to copy non null properties from object to another

Viewed 50778

See the following class

public class Parent {

    private String name;
    private int age;
    private Date birthDate;

    // getters and setters   

}

Suppose I have created a parent object as follows

Parent parent = new Parent();

parent.setName("A meaningful name");
parent.setAge(20);

Notice according to code above birthDate property is null. Now I want to copy only non-null properties from parent object to another. Something like

SomeHelper.copyNonNullProperties(parent, anotherParent);

I need it because I want to update anotherParent object without overwriting its non-null with null values.

Do you know some helper like this one?

I accept minimal code as answer whether no helper in mind

9 Answers

If your setter's return type is not void, BeanUtils of Apache will not work, spring can. So combine the two.

package cn.corpro.bdrest.util;

import org.apache.commons.beanutils.BeanUtilsBean;
import org.apache.commons.beanutils.ConvertUtilsBean;
import org.apache.commons.beanutils.PropertyUtilsBean;
import org.springframework.beans.BeanUtils;

import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;

/**
 * Author: BaiJiFeiLong@gmail.com
 * DateTime: 2016/10/20 10:17
 */
public class MyBeanUtils {

    public static void copyPropertiesNotNull(Object dest, Object orig) throws InvocationTargetException, IllegalAccessException {
        NullAwareBeanUtilsBean.getInstance().copyProperties(dest, orig);
    }

    private static class NullAwareBeanUtilsBean extends BeanUtilsBean {

        private static NullAwareBeanUtilsBean nullAwareBeanUtilsBean;

        NullAwareBeanUtilsBean() {
            super(new ConvertUtilsBean(), new PropertyUtilsBean() {
                @Override
                public PropertyDescriptor[] getPropertyDescriptors(Class<?> beanClass) {
                    return BeanUtils.getPropertyDescriptors(beanClass);
                }

                @Override
                public PropertyDescriptor getPropertyDescriptor(Object bean, String name) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
                    return BeanUtils.getPropertyDescriptor(bean.getClass(), name);
                }
            });
        }

        public static NullAwareBeanUtilsBean getInstance() {
            if (nullAwareBeanUtilsBean == null) {
                nullAwareBeanUtilsBean = new NullAwareBeanUtilsBean();
            }
            return nullAwareBeanUtilsBean;
        }

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

I landed here after many years finding a solution, used simple java reflection to achieve it. Hope it helps!

public static void copyDiff(Product destination, Product source) throws  
             IllegalAccessException, NoSuchFieldException {
for (Field field : source.getClass().getDeclaredFields()) {
    field.setAccessible(true);
    String name = field.getName();
    Object value = field.get(source);

    //If it is a non null value copy to destination
    if (null != value) 
    {

         Field destField = destination.getClass().getDeclaredField(name);
         destField.setAccessible(true);

         destField.set(destination, value);
    }
    System.out.printf("Field name: %s, Field value: %s%n", name, value);
   }
}

Here's my adaptation to copy non-null properties including ignoring properties as well using Spring BeanUtils.

package com.blah;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import java.beans.PropertyDescriptor;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import javax.annotation.Nonnull;

/**
 * brett created on 10/1/20.
 * <p>
 * Modified from: https://codippa.com/skip-null-properties-spring-beanutils/
 */
public final class NullAwareBeanUtils {

    private NullAwareBeanUtils() {}

    /**
     * Copies non-null properties from one object to another.
     *
     * @param source
     * @param destination
     * @param ignoreProperties
     */
    public static void copyNonNullProperties(Object source, Object destination, String... ignoreProperties) {
        final Set<String> ignoreAllProperties = new HashSet<>();
        ignoreAllProperties.addAll(getPropertyNamesWithNullValue(source));
        ignoreAllProperties.addAll(Arrays.asList(ignoreProperties));

        BeanUtils.copyProperties(source, destination, ignoreAllProperties.toArray(new String[]{}));
    }

    @Nonnull
    private static Set<String> getPropertyNamesWithNullValue(Object source) {
        final BeanWrapper sourceBeanWrapper = new BeanWrapperImpl(source);
        final java.beans.PropertyDescriptor[] propertyDescriptors = sourceBeanWrapper.getPropertyDescriptors();
        final Set<String> emptyNames = new HashSet();

        for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
            // Check if value of this property is null then add it to the collection
            Object propertyValue = sourceBeanWrapper.getPropertyValue(propertyDescriptor.getName());
            if (propertyValue != null) continue;

            emptyNames.add(propertyDescriptor.getName());
        }

        return emptyNames;
    }
}

A solution relying exclusively on field manipulation through reflection with no third-party dependencies:

public final class NonNullFieldCopier {

    private NonNullFieldCopier() {
    }

    public static <T> void copyNonNull(T to, T from) throws IllegalAccessException {
        if (!to.getClass().equals(from.getClass())) {
            throw new IllegalArgumentException(to.getClass() + " is of a different type than " + from.getClass());
        }
        final List<Field> fields = getAllModelFields(from.getClass());
        for (Field field : fields) {
            field.setAccessible(true);
            final Object fieldValue = field.get(from);
            if (fieldValue != null) {
                field.set(to, fieldValue);
            }
        }
    }

    private static List<Field> getAllModelFields(Class<?> clazz) {
        List<Field> fields = new ArrayList<>();
        do {
            Collections.addAll(fields, clazz.getDeclaredFields());
            clazz = clazz.getSuperclass();
        } while (clazz != null);
        return fields;
    }

}
Related