50

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

Mark Rotteveel
  • 82,132
  • 136
  • 114
  • 158
Arthur Ronald
  • 31,649
  • 18
  • 106
  • 135

8 Answers8

102

I supose you already have a solution, since a lot of time has happened since you asked. However, it is not marked as solved, and maybe I can help other users.

Have you tried by defining a subclass of the BeanUtilsBean of the org.commons.beanutils package? Actually, BeanUtils uses this class, so this is an improvement of the solution proposed by dfa.

Checking at the source code of that class, I think you can overwrite the copyProperty method, by checking for null values and doing nothing if the value is null.

Something like this :

package foo.bar.copy;
import java.lang.reflect.InvocationTargetException;
import org.apache.commons.beanutils.BeanUtilsBean;

public class NullAwareBeanUtilsBean extends BeanUtilsBean{

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

}

Then you can just instantiate a NullAwareBeanUtilsBean and use it to copy your beans, for example:

BeanUtilsBean notNull=new NullAwareBeanUtilsBean();
notNull.copyProperties(dest, orig);
alain.janinm
  • 19,035
  • 10
  • 56
  • 103
SergiGS
  • 1,036
  • 1
  • 8
  • 3
  • an ideal answer would use the Parent class example provided in the question – kiedysktos Dec 12 '16 at 16:11
  • I'm trying to implement your ideas but no success so far. I created a thread for this http://stackoverflow.com/questions/41125384/copy-non-null-properties-from-one-object-to-another-using-beanutilsbean – kiedysktos Dec 13 '16 at 16:01
  • 1
    copyProperty() is static method http://commons.apache.org/proper/commons-beanutils/javadocs/v1.9.3/apidocs/index.html – Sadanand Feb 27 '17 at 03:52
5

Using PropertyUtils (commons-beanutils)

for (Map.Entry<String, Object> e : PropertyUtils.describe(parent).entrySet()) {
         if (e.getValue() != null && !e.getKey().equals("class")) {
                PropertyUtils.setProperty(anotherParent, e.getKey(), e.getValue());
         }
}

in Java8:

    PropertyUtils.describe(parent).entrySet().stream()
        .filter(e -> e.getValue() != null)
        .filter(e -> ! e.getKey().equals("class"))
        .forEach(e -> {
        try {
            PropertyUtils.setProperty(anotherParent, e.getKey(), e.getValue());
        } catch (Exception e) {
            // Error setting property ...;
        }
    });
3

Simply use your own copy method:

void copy(Object dest, Object source) throws IntrospectionException, IllegalArgumentException, IllegalAccessException,
        InvocationTargetException {
    BeanInfo beanInfo = Introspector.getBeanInfo(source.getClass());
    PropertyDescriptor[] pdList = beanInfo.getPropertyDescriptors();
    for (PropertyDescriptor pd : pdList) {
        Method writeMethod = null;
        Method readMethod = null;
        try {
            writeMethod = pd.getWriteMethod();
            readMethod = pd.getReadMethod();
        } catch (Exception e) {
        }

        if (readMethod == null || writeMethod == null) {
            continue;
        }

        Object val = readMethod.invoke(source);
        writeMethod.invoke(dest, val);
    }
}
Mohsen
  • 3,412
  • 3
  • 31
  • 58
2

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);
        }
    }
}
BaiJiFeiLong
  • 1,781
  • 1
  • 13
  • 18
1

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);
   }
}
samairtimer
  • 706
  • 1
  • 11
  • 23
0

I know the fact that this question is pretty old, but I thought the below answer may be useful for someone.

If you use Spring, you may try the below option.

import java.beans.PropertyDescriptor;
import java.util.HashSet;
import java.util.Set;

import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;

/**
 * Helper class to extract property names from an object.
 * 
 * @Threadsafe
 * 
 * @author arun.bc
 * 
 */
public class PropertyUtil {

    /**
     * Gets the properties which have null values from the given object.
     * 
     * @param - source object
     * 
     * @return - String array of property names.
     */
    public static String[] getNullPropertiesString(Object source) {
        Set<String> emptyNames = getNullProperties(source);
        String[] result = new String[emptyNames.size()];

        return emptyNames.toArray(result);
    }


    /**
     * Gets the properties which have null values from the given object.
     * 
     * @param - source object
     * 
     * @return - Set<String> of property names.
     */
    public static Set<String> getNullProperties(Object source) {
        final BeanWrapper src = new BeanWrapperImpl(source);
        PropertyDescriptor[] pds = src.getPropertyDescriptors();

        Set<String> emptyNames = new HashSet<String>();
        for (PropertyDescriptor pd : pds) {
            Object srcValue = src.getPropertyValue(pd.getName());
            if (srcValue == null)
                emptyNames.add(pd.getName());
        }
        return emptyNames;
    }

    /**
     * Gets the properties which are not null from the given object.
     * 
     * @param - source object
     * 
     * @return - Set<String> array of property names.
     */
    public static Set<String> getNotNullProperties(Object source) {
        final BeanWrapper src = new BeanWrapperImpl(source);
        PropertyDescriptor[] pds = src.getPropertyDescriptors();

        Set<String> names = new HashSet<String>();
        for (PropertyDescriptor pd : pds) {
            Object srcValue = src.getPropertyValue(pd.getName());
            if (srcValue != null)
                names.add(pd.getName());
        }

        return names;
    }
}

Again you may use PropertyDescriptor and the Set from the above methods to modify the object.

Arun Chandrasekaran
  • 2,111
  • 19
  • 32
0

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

Brett Cooper
  • 1,450
  • 1
  • 7
  • 4
-1

you can use Apache Common BeanUtils, more specifically the copyProperties helper in BeanUtils class:

 BeanUtils.copyProperties(parent, anotherParent);   

however why do you want copy only non-null properties? if a property in parent is null, by simply copying it you have null also in anotherParent right?

Just guessing... you want to update a bean with another bean?

dfa
  • 107,531
  • 29
  • 184
  • 223
  • 3
    Yes, i want to update another bean without overrides its non null values. – Arthur Ronald Aug 19 '09 at 18:43
  • 2
    +1 for a sane suggestion for using libraries... sometimes I feel like we're the lone voices in the wilderness on this one – skaffman Aug 19 '09 at 18:44
  • 5
    Hi, sorry but it does not work. If i have a non null anotherParent.getBirthDate() and i call BeanUtils.copyProperties(parent, anotherParent) anotherParent.getBirthDate() will return null – Arthur Ronald Aug 19 '09 at 19:13