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

1
  • Can you not include BeanUtils as part of your Spring project class path? I don't think Spring's BeanUtils work this way. Commented Nov 2, 2013 at 0:37

6 Answers 6

87

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));
}
Sign up to request clarification or add additional context in comments.

3 Comments

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
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.
There's typo in call sig of myCopyProps method... extra comma after Object
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);
}

Comments

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

1 Comment

Its not working,, I tried. Its replacing the values if it find otherwise it creates new.
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);
        }
    }
    

Comments

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 Comments

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 :)
...especially as it's 99% similar to a answer posted above (4 years earlier): stackoverflow.com/a/32066155
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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.