48

I would like to know how to copy the properties from an Object Source to an Object Dest ignoring null values​​ using Spring Framework.

I actually use Apache beanutils, with this code

    beanUtils.setExcludeNulls(true);
    beanUtils.copyProperties(dest, source);

to do it. But now i need to use Spring.

Any help?

Thx a lot

Cœur
  • 37,241
  • 25
  • 195
  • 267
Fingolricks
  • 1,196
  • 2
  • 14
  • 30

6 Answers6

86

You can create your own method to copy properties while ignoring null values.

public static String[] getNullPropertyNames (Object source) {
    final BeanWrapper src = new BeanWrapperImpl(source);
    java.beans.PropertyDescriptor[] pds = src.getPropertyDescriptors();

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

    String[] result = new String[emptyNames.size()];
    return emptyNames.toArray(result);
}

// then use Spring BeanUtils to copy and ignore null using our function
public static void myCopyProperties(Object src, Object target) {
    BeanUtils.copyProperties(src, target, getNullPropertyNames(src));
}
mohkamfer
  • 435
  • 3
  • 12
Alfred Xiao
  • 1,758
  • 15
  • 16
  • This is a very good solution for it. I hoped that the Framework had already scheduled a way that will take care of that, but this will work perfectly. thanks – Fingolricks Nov 04 '13 at 03:14
  • I did the same. I included these methods in a new class with some other custom implementations. My new class extends BeanUtils class so that I can add additional features and functionalities. – yuva Nov 05 '15 at 09:01
  • There's typo in call sig of myCopyProps method... extra comma after Object – Adam Hughes Feb 11 '16 at 14:25
55

Java 8 version of getNullPropertyNames method from alfredx's post:

public static String[] getNullPropertyNames(Object source) {
    final BeanWrapper wrappedSource = new BeanWrapperImpl(source);
    return Stream.of(wrappedSource.getPropertyDescriptors())
            .map(FeatureDescriptor::getName)
            .filter(propertyName -> wrappedSource.getPropertyValue(propertyName) == null)
            .toArray(String[]::new);
}
Community
  • 1
  • 1
Paweł Kaczorowski
  • 1,502
  • 2
  • 14
  • 25
8

I advise you to use ModelMapper.

This is a sample code that solves your doubt.

      ModelMapper modelMapper = new ModelMapper();
modelMapper.getConfiguration().setSkipNullEnabled(true).setMatchingStrategy(MatchingStrategies.STRICT);

      Company a = new Company();
      a.setId(123l);
      Company b = new Company();
      b.setId(456l);
      b.setName("ABC");

      modelMapper.map(a, b);

      System.out.println("->" + b.getName());

It should print the B value. But if you set the "A" name, the result is a print of "A" value.

The secret is changing the value of SkipNullEnabled to true.

ModelMapper

ModelMapper MVN

bpedroso
  • 4,617
  • 3
  • 29
  • 34
  • Its not working,, I tried. Its replacing the values if it find otherwise it creates new. – Abdul Apr 06 '19 at 05:03
5

SpringBeans.xml

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

    <bean id="source" class="com.core.HelloWorld">
        <property name="name" value="Source" />
        <property name="gender" value="Male" />
    </bean>

    <bean id="target" class="com.core.HelloWorld">
        <property name="name" value="Target" />
    </bean>

</beans>
  1. Create a java Bean,

    public class HelloWorld {
        private String name;
        private String gender;
    
        public void printHello() {
            System.out.println("Spring 3 : Hello ! " + name + "    -> gender      -> " + gender);
        }
    
        //Getters and Setters
    }
    
  2. Create Main Class to test

    public class App {
        public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext("SpringBeans.xml");
    
            HelloWorld source = (HelloWorld) context.getBean("source");
            HelloWorld target = (HelloWorld) context.getBean("target");
    
            String[] nullPropertyNames = getNullPropertyNames(target);
            BeanUtils.copyProperties(target,source,nullPropertyNames);
            source.printHello();
        }
    
        public static String[] getNullPropertyNames(Object source) {
            final BeanWrapper wrappedSource = new BeanWrapperImpl(source);
            return Stream.of(wrappedSource.getPropertyDescriptors())
                .map(FeatureDescriptor::getName)
                .filter(propertyName -> wrappedSource.getPropertyValue(propertyName) == null)
                .toArray(String[]::new);
        }
    }
    
Kumar Abhishek
  • 3,004
  • 33
  • 29
1
public static List<String> getNullProperties(Object source) {
    final BeanWrapper wrappedSource = new BeanWrapperImpl(source);
    return Stream.of(wrappedSource.getPropertyDescriptors())
        .map(FeatureDescriptor::getName)
        .filter(propertyName -> Objects.isNull(wrappedSource.getPropertyValue(propertyName)))
        .collect(Collectors.toList());
  • 2
    It would probably be best to add some form of explanation to your answer; that will improve its quality and make it more likely to get upvotes :) – Das_Geek Sep 19 '19 at 23:01
  • 1
    ...especially as it's 99% similar to a answer posted above (4 years earlier): https://stackoverflow.com/a/32066155 – maxxyme Dec 15 '21 at 09:53
1

A better solution based on Pawel Kaczorowski's answer:

public static String[] getNullPropertyNames(Object source) {
    final BeanWrapper wrappedSource = new BeanWrapperImpl(source);
    return Stream.of(wrappedSource.getPropertyDescriptors())
        .map(FeatureDescriptor::getName)
        .filter(propertyName -> {
            try {
               return wrappedSource.getPropertyValue(propertyName) == null
            } catch (Exception e) {
               return false
            }                
        })
        .toArray(String[]::new);
}

For example, if we have a DTO:

class FooDTO {
    private String a;
    public String getA() { ... };
    public String getB();
}

Other answers will throw exceptions on this special case.

sadhen
  • 462
  • 4
  • 14