Is it possible automatically instantiation of a nested Property with Commons Bean Utils?

橙三吉。 提交于 2019-12-07 04:29:39

问题


I'm using PropertyUtils.setProperty(object, name, value) method of Apache Commons Bean Utils:

Giving these classes:

public class A {
    B b;
}

public class B {
    C c;
}

public class C {
}

And this:

A a = new A();
C c = new C();
PropertyUtils.setProperty(a, "b.c", c); //exception

If I try that I get: org.apache.commons.beanutils.NestedNullException: Null property value for 'b.c' on bean class 'class A'

Is it possible to tell PropertyUtils that if a nested property has a null value try to instantiate it (default constructor) before trying to go deeper?

Any other approach?

Thank you


回答1:


I solved it by doing this:

private void instantiateNestedProperties(Object obj, String fieldName) {
    try {
        String[] fieldNames = fieldName.split("\\.");
        if (fieldNames.length > 1) {
            StringBuffer nestedProperty = new StringBuffer();
            for (int i = 0; i < fieldNames.length - 1; i++) {
                String fn = fieldNames[i];
                if (i != 0) {
                    nestedProperty.append(".");
                }
                nestedProperty.append(fn);

                Object value = PropertyUtils.getProperty(obj, nestedProperty.toString());

                if (value == null) {
                    PropertyDescriptor propertyDescriptor = PropertyUtils.getPropertyDescriptor(obj, nestedProperty.toString());
                    Class<?> propertyType = propertyDescriptor.getPropertyType();
                    Object newInstance = propertyType.newInstance();
                    PropertyUtils.setProperty(obj, nestedProperty.toString(), newInstance);
                }
            }
        }
    } catch (IllegalAccessException e) {
        throw new RuntimeException(e);
    } catch (InvocationTargetException e) {
        throw new RuntimeException(e);
    } catch (NoSuchMethodException e) {
        throw new RuntimeException(e);
    } catch (InstantiationException e) {
        throw new RuntimeException(e);
    }
}



回答2:


I know the question is about apache commons PropertyUtils.setProperty but there is very similar functionality available in Spring Expression Language "SpEL" which does exactly what you want. Better still it deals with lists and arrays too. The doc link above is for spring 4.x but the code below works for me in spring 3.2.9.

    StockOrder stockOrder = new StockOrder(); // Your root class here

    SpelParserConfiguration config = new SpelParserConfiguration(true,true);   // auto create objects if null
    ExpressionParser parser = new SpelExpressionParser(config);
    StandardEvaluationContext modelContext = new StandardEvaluationContext(stockOrder);

    parser.parseExpression("techId").setValue(modelContext, "XXXYYY1");
    parser.parseExpression("orderLines[0].partNumber").setValue(modelContext, "65498");
    parser.parseExpression("orderLines[0].inventories[0].serialNumber").setValue(modelContext, "54686513216");

    System.out.println(ReflectionToStringBuilder.toString(stockOrder));



回答3:


A little correction:

String fn = fieldNames[i];
if (i != 0) {
        nestedProperty.append(".");
}
nestedProperty.append(fn);
Object value = PropertyUtils.getProperty(obj, nestedProperty.toString());



回答4:


I have used only reflection w/o Apache library to achieve this. The assumption is that all object to be traversed are all POJOs, and default construction is publicly accessible. This way, there is no need to construct the reference path for each loop.

public Object getOrCreateEmbeddedObject(Object inputObj,String[] fieldNames) throws Exception {

    Object cursor = inputObj;

    //Loop until second last index
    for (int i = 0; i < fieldNames.length - 1; i++){
        Field ff = getClassFieldFrom(cursor,fieldNames[i]);
        Object child = ff.get(cursor);
        if(null == child) {
            Class<?> cls=ff.getType();
            child = cls.newInstance();
            ff.set(cursor, child);
        }
        cursor = child;
    }

    return cursor;
}

private Field getClassFieldFrom(Object object, String fieldStr)
            throws NoSuchFieldException {
        java.lang.reflect.Field ff = object.getClass().getDeclaredField(fieldStr);
        ff.setAccessible(true);
        return ff;
}

If you have any suggestion to my solution , please let me know.




回答5:


I went for the very basic approach of just instantiating each of the objects by default:

public class A {
    B b = new B();
}

public class B {
    C c = new C();
}

public class C {
}

Not ideal, but it worked for my situation and didn't involve complicated fixes.




回答6:


After doing some research, the short answer to "Is it possible..." question is No.



来源:https://stackoverflow.com/questions/4877729/is-it-possible-automatically-instantiation-of-a-nested-property-with-commons-bea

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!