6

我正在使用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 类 'class A '上的 'bc' 的 Null 属性值

是否可以告诉 PropertyUtils 如果嵌套属性具有空值,请在尝试更深入之前尝试实例化它(默认构造函数)?

还有其他方法吗?

谢谢

4

7 回答 7

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);
    }
}
于 2011-02-02T23:16:04.500 回答
8

我知道问题是关于 apache commons PropertyUtils.setProperty 但在Spring Expression Language “SpEL ” 中有非常相似的功能 ,它完全符合你的要求。更好的是它也处理列表和数组。上面的文档链接适用于 spring 4.x,但下面的代码适用于 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));
于 2016-12-27T23:24:45.867 回答
2

一点修正:

String fn = fieldNames[i];
if (i != 0) {
        nestedProperty.append(".");
}
nestedProperty.append(fn);
Object value = PropertyUtils.getProperty(obj, nestedProperty.toString());
于 2011-02-25T10:06:18.757 回答
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;
}

如果您对我的解决方案有任何建议,请告诉我。

于 2015-07-28T02:06:43.967 回答
0

我采用了默认情况下仅实例化每个对象的非常基本的方法:

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

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

public class C {
}

不理想,但它适用于我的情况并且不涉及复杂的修复。

于 2015-10-16T13:54:42.567 回答
0

恕我直言,最好的解决方案是摆脱 commons-beanutils 并使用 Spring Framework org.springframework.beans.PropertyAccessorFactory

BeanWrapper wrapper = PropertyAccessorFactory.forBeanPropertyAccess(targetObject);
wrapper.setAutoGrowNestedPaths(true);

我不会深入研究它的工作原理,但如果你想查看它,请查看上面的链接,这个 API 非常直观,但你需要在你的classpath,所以我不建议你仅仅为了这个特性而添加 spring。

然而

如果您只有commons-beanutils作为您的盟友,那么以下代码片段可能会帮助您在设置值时增加嵌套路径,因此,您无需关注路径属性中的空对象。

在此示例中,我使用 JPA Tuple Query 构造了一个自定义对象,该对象具有一些特定的属性路径以及要设置的相应值。

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

于 2020-11-23T20:38:16.203 回答
-1

在做了一些研究之后,“Is it possible...”问题的简短回答是No

于 2011-02-02T18:31:30.990 回答