我正在使用Apache Commons Bean Utils的 PropertyUtils.setProperty(object,name,value)方法:
提供这些课程:
public class A {
B b;
}
public class B {
C c;
}
public class C {
}
而且:
A a = new A();
C c = new C();
PropertyUtils.setProperty(a, "b.c", c); //exception
如果我尝试我得到: org.apache.commons.beanutils.NestedNullException:bean类“A类”上的'b.c'的空属性值
是否有可能告诉PropertyUtils,如果嵌套属性具有空值,尝试在尝试深入之前实例化它(默认构造函数)?
还有其他办法吗?
谢谢
答案 0 :(得分:9)
我这样解决了这个问题:
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);
}
}
答案 1 :(得分:5)
我知道问题是关于apache commons PropertyUtils.setProperty,但是有非常类似的功能 Spring Expression Language "SpEL“它完全符合你的要求。更好的是它也处理列表和数组。上面的doc链接适用于spring 4.x但是下面的代码适用于我在春季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));
答案 2 :(得分:2)
稍作修正:
String fn = fieldNames[i];
if (i != 0) {
nestedProperty.append(".");
}
nestedProperty.append(fn);
Object value = PropertyUtils.getProperty(obj, nestedProperty.toString());
答案 3 :(得分:0)
我只使用了没有Apache库的反射来实现这一点。假设所有要遍历的对象都是POJO,默认构造是公共可访问的。这样,就不需要为每个循环构造引用路径。
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;
}
如果您对我的解决方案有任何建议,请告诉我。
答案 4 :(得分:0)
我选择了默认情况下实例化每个对象的基本方法:
public class A {
B b = new B();
}
public class B {
C c = new C();
}
public class C {
}
不理想,但它适用于我的情况,并没有涉及复杂的修复。
答案 5 :(得分:0)
恕我直言,最好的解决方案是摆脱commons-beanutils并使用Spring Framework org.springframework.beans.PropertyAccessorFactory
BeanWrapper wrapper = PropertyAccessorFactory.forBeanPropertyAccess(targetObject);
wrapper.setAutoGrowNestedPaths(true);
我不会深入研究它的工作原理,但是如果您想查看它,请看一下上面的链接,该API非常直观,但是您需要具有Spring Framework Core是在您的类路径中配置的,因此我不建议您仅出于此功能而添加spring。
但是,
如果您只有 commons-beanutils 作为盟友,则以下代码段可帮助您在设置值时扩展嵌套路径,因此,您无需担心路径属性中的空对象。
在此示例中,我与JPA元组查询一起使用,构造了一个带有一些特定属性路径的自定义对象,并设置了相应的值。
import java.util.ArrayList;
import java.util.List;
import javax.persistence.Tuple;
import javax.persistence.TupleElement;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.beanutils.expression.DefaultResolver;
public class TupleToObject<T> {
public List<T> transformResult(List<Tuple> result, Class<T> targetClass) {
try {
List<T> objects = new ArrayList<>();
for (Tuple tuple : result) {
T target = targetClass.newInstance();
List<TupleElement<?>> elements = tuple.getElements();
for (TupleElement<?> tupleElement : elements) {
String alias = tupleElement.getAlias();
Object value = tuple.get(alias);
if (value != null) {
instantiateObject(target, alias);
PropertyUtils.setNestedProperty(target, alias, value);
}
}
objects.add(target);
}
return objects;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private void instantiateObject(T target, String propertyPath) throws Exception {
DefaultResolver resolver = new DefaultResolver();
Object currentTarget = target;
while (resolver.hasNested(propertyPath)) {
final String property = resolver.next(propertyPath);
Object value = PropertyUtils.getSimpleProperty(currentTarget, property);
if (value == null) {
Class<?> propertyType = PropertyUtils.getPropertyType(currentTarget, property);
value = propertyType.newInstance();
PropertyUtils.setSimpleProperty(currentTarget, property, value);
}
currentTarget = value;
propertyPath = resolver.remove(propertyPath);
}
}
}
此代码使用commons-beanutils-1.9.3.jar
答案 6 :(得分:-1)
在做了一些研究之后,“是否有可能......”的简短回答是否。