Search code examples
javaxstreamapache-commons-beanutils

BeanUtils.copyProperties ignoring null values


I have the following class:

import org.apache.commons.beanutils.BeanUtils;
import com.thoughtworks.xstream.XStream;
...

public class MyBean {
    protected static final XStream XSTREAM = new XStream(new DomDriver());

    protected String name;
    protected Something something;

    public MyBean() {
        something = new Something();
    }

    public MyBean(String xml) {
        this();
        MyBean beanFromXML = (MyBean) XSTREAM.fromXML(new StringReader(xml));
        BeanUtils.copyProperties(this, beanFromXML);
    }

    public String toString() {
        return XSTREAM.toXML(this);
    }

    // Getters and setters...
}

It's a bean with ability to serialize and deserialize to/from XML using XStream.

I also added a non-args constructor that initializes something, to avoid null pointer errors - the bean is actually a lot more complex, and I don't want to be checking "is something != null?" a million times.

The problem arises when I use the XML-constructor. Lets say I've the following XML:

<myBean>
    <name>John</name>
</myBean>

This is what I would like the constructor to do:

name: "John";
something: new Something();

However, since there is no <something> element in the XML, BeanUtils.copyProperties makes something = null;, thus what I get is:

 name: "John"
 something: null

How can I copy beanFromXML's properties into this... but ignoring the null properties instead of overwriting them?


Solution

  • You can create a custom converter that creates a default value for null properties:

    public class MyNullConverter implements Converter {
      @Override
      public Object convert(final Class type, final Object value) {
        try {
          return value == null ? type.newInstance() : value;
        } catch (final InstantiationException e) {
          return null;
        } catch (final IllegalAccessException e) {
          return null;
        }
      }
    }
    

    Then register it for bean classes you want default (empty) values:

    ConvertUtils.register(new MyNullConverter(), Something.class);
    

    Your code will now work. The only thing that might bug you, is that your Something gets initialized twice. Don't know if this is OK...

    BTW, if you want a more fine grained control over the process: use BeanUtilsBean, PropertyUtilsBean, and ConvertUtilsBean instead.