Java Beans, BeanUtils, and the Boolean wrapper class
Asked Answered
N

3

17

I'm using BeanUtils to manipulate Java objects created via JAXB, and I've run into an interesting issue. Sometimes, JAXB will create a Java object like this:

public class Bean {
    protected Boolean happy;

    public Boolean isHappy() {
        return happy;
    }

    public void setHappy(Boolean happy) {
        this.happy = happy;
    }
}

The following code works just fine:

Bean bean = new Bean();
BeanUtils.setProperty(bean, "happy", true);

However, attempting to get the happy property like so:

Bean bean = new Bean();
BeanUtils.getProperty(bean, "happy");

Results in this exception:

Exception in thread "main" java.lang.NoSuchMethodException: Property 'happy' has no getter method in class 'class Bean'

Changing everything to a primitive boolean allows both the set and get call to work. I don't have this option, however, since these are generated classes. I assume this happens because the Java Bean libraries only consider an is<name> method to represent a property if the return type is a primitive boolean, and not the wrapper type Boolean. Does anyone have a suggestion as to how to access properties like these through BeanUtils? Is there some kind of workaround I can use?

Nettienetting answered 10/3, 2011 at 22:10 Comment(4)
Where does the BeanUtils class come from? I checked with org.apache.commons.beanutils.BeanUtils (1.8.3) and it works fine. Please note that typically is prefix is used for boolean, while for Boolean: get.Autoionization
I'm using the same BeanUtils. You were able to do a getProperty() with an is method that returns the Boolean wrapper class?Nettienetting
Your assumption about is<name> is correct. isXXX only applys to type boolean return type. For Boolean return type, getXXX is the correct method name.Carr
you're right, getProperty() does not work with Boolean is. In fact, IntelliJ generates getters with get for Boolean and is for boolean - I guess Eclipse does the same.Autoionization
A
10

Finally I've found legal confirmation:

8.3.2 Boolean properties

In addition, for boolean properties, we allow a getter method to match the pattern:

public boolean is<PropertyName>();

From JavaBeans specification. Are you sure you haven't came across JAXB-131 bug?

Autoionization answered 11/3, 2011 at 17:21 Comment(1)
Yep, looks like I was running into that bug. I upgraded my JAXB version to 2.1.13 and added the "-enableIntrospection" flag. That changed all the Boolean wrapper is methods to gets. Thanks!Nettienetting
H
8

Workaround to handle Boolean isFooBar() case with BeanUtils

  1. Create new BeanIntrospector

private static class BooleanIntrospector implements BeanIntrospector{
    @Override
    public void introspect(IntrospectionContext icontext) throws IntrospectionException {
        for (Method m : icontext.getTargetClass().getMethods()) {
            if (m.getName().startsWith("is") && Boolean.class.equals(m.getReturnType())) {
                String propertyName = getPropertyName(m);
                PropertyDescriptor pd = icontext.getPropertyDescriptor(propertyName);

                if (pd == null)
                    icontext.addPropertyDescriptor(new PropertyDescriptor(propertyName, m, getWriteMethod(icontext.getTargetClass(), propertyName)));
                else if (pd.getReadMethod() == null)
                    pd.setReadMethod(m);

            }
        }
    }

    private String getPropertyName(Method m){
        return WordUtils.uncapitalize(m.getName().substring(2, m.getName().length()));
    }

    private Method getWriteMethod(Class<?> clazz, String propertyName){
        try {
            return clazz.getMethod("get" + WordUtils.capitalize(propertyName));
        } catch (NoSuchMethodException e) {
            return null;
        }
    }
}

  1. Register BooleanIntrospector:

    BeanUtilsBean.getInstance().getPropertyUtils().addBeanIntrospector(new BooleanIntrospector());

Holystone answered 24/4, 2014 at 22:35 Comment(1)
Good stuff! I think there are 2 changes needed though: in getWriteMethod, it should be "set" - and also, I was getting MethodNotFoundExceptions until I changed to the overloaded method that receives a parameter type: return clazz.getMethod("set" + WordUtils.capitalize(propertyName), Boolean.class);Cheekbone
C
0

you can just create second getter with SET - sufix as workaround :)

Corposant answered 4/6, 2018 at 21:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.