BeanUtils.copyProperties ignoring null values

AJPerez picture AJPerez · Dec 10, 2010 · Viewed 8.6k times · Source

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?

Answer

Eran Harel picture Eran Harel · Dec 10, 2010

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.