0%

BeanUtils.copyProperties() 如何忽略值为null的字段

BeanUtils.copyProperties() 会将所有字段的值从一个对象拷贝到另外一个对应,如果希望只拷贝源对象中值不为null的字段,可以通过如下方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/**
* 获取对象中值为null的属性
*
* @param source 对象
* @return String数组,值为null的属性名字
*/
public static String[] getNullPropertyNames(Object source) {
final BeanWrapper src = new BeanWrapperImpl(source);
java.beans.PropertyDescriptor[] pds = src.getPropertyDescriptors();

Set<String> emptyNames = new HashSet<>();
for (java.beans.PropertyDescriptor pd : pds) {
Object srcValue = src.getPropertyValue(pd.getName());
if (srcValue == null) {
emptyNames.add(pd.getName());
}
}

String[] result = new String[emptyNames.size()];
return emptyNames.toArray(result);
}

/**
* 拷贝对象,忽略值为null的字段
*/
public static void copyPropertiesIgnoreNull(Object src, Object target) {
BeanUtils.copyProperties(src, target, getNullPropertyNames(src));
}

getNullPropertyNames() Java8 版本如下,和上面用哪个都行

1
2
3
4
5
6
7
public static String[] getNullPropertyNames(Object source) {
final BeanWrapper wrappedSource = new BeanWrapperImpl(source);
return Stream.of(wrappedSource.getPropertyDescriptors())
.map(FeatureDescriptor::getName)
.filter(propertyName -> wrappedSource.getPropertyValue(propertyName) == null)
.toArray(String[]::new);
}

有一个小坑提醒一下大家,假如有如下两个对象的值(Json 表示)如下:
source

1
2
3
4
5
6
7
8
9
{
"fieldA": 111,
"fieldB": {
"subFieldA": 222,
"subFieldB": null,
"subFieldC": null
},
"fieldC": null
}

target

1
2
3
4
5
6
7
8
9
{
"fieldA": 0,
"fieldB": {
"subFieldA": 0,
"subFieldB": 333,
"subFieldC": 444
},
"fieldC": 555
}

然后执行copyPropertiesIgnoreNull(source, target),有的同学可能期望的结果是

1
2
3
4
5
6
7
8
9
{
"fieldA": 111,
"fieldB": {
"subFieldA": 222,
"subFieldB": 333,
"subFieldC": 444
},
"fieldC": 555
}

但实际结果却是

1
2
3
4
5
6
7
8
9
{
"fieldA": 111,
"fieldB": {
"subFieldA": 222,
"subFieldB": null,
"subFieldC": null
},
"fieldC": 555
}

因为source中的fieldB是不为null的,所以在拷贝的时候会将整个fieldB的值拷贝到目标对象中,而不是递归的判断字段内部的字段是否为null !