This commit is contained in:
Looly
2022-06-07 15:31:55 +08:00
parent 084870261f
commit 9b9b35ffc2
22 changed files with 165 additions and 122 deletions

View File

@@ -7,6 +7,7 @@ import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.SetUtil; import cn.hutool.core.collection.SetUtil;
import cn.hutool.core.convert.Convert; import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.func.Editor; import cn.hutool.core.lang.func.Editor;
import cn.hutool.core.lang.mutable.MutableEntry;
import cn.hutool.core.map.CaseInsensitiveMap; import cn.hutool.core.map.CaseInsensitiveMap;
import cn.hutool.core.map.MapUtil; import cn.hutool.core.map.MapUtil;
import cn.hutool.core.reflect.ClassUtil; import cn.hutool.core.reflect.ClassUtil;
@@ -399,7 +400,7 @@ public class BeanUtil {
* @return Bean * @return Bean
*/ */
public static <T> T fillBeanWithMap(final Map<?, ?> map, final T bean, final boolean isToCamelCase, final boolean isIgnoreError) { public static <T> T fillBeanWithMap(final Map<?, ?> map, final T bean, final boolean isToCamelCase, final boolean isIgnoreError) {
return fillBeanWithMap(map, bean, isToCamelCase, CopyOptions.create().setIgnoreError(isIgnoreError)); return fillBeanWithMap(map, bean, isToCamelCase, CopyOptions.of().setIgnoreError(isIgnoreError));
} }
/** /**
@@ -412,7 +413,7 @@ public class BeanUtil {
* @return Bean * @return Bean
*/ */
public static <T> T fillBeanWithMapIgnoreCase(final Map<?, ?> map, final T bean, final boolean isIgnoreError) { public static <T> T fillBeanWithMapIgnoreCase(final Map<?, ?> map, final T bean, final boolean isIgnoreError) {
return fillBeanWithMap(map, bean, CopyOptions.create().setIgnoreCase(true).setIgnoreError(isIgnoreError)); return fillBeanWithMap(map, bean, CopyOptions.of().setIgnoreCase(true).setIgnoreError(isIgnoreError));
} }
/** /**
@@ -527,15 +528,19 @@ public class BeanUtil {
*/ */
public static Map<String, Object> beanToMap(final Object bean, final String... properties) { public static Map<String, Object> beanToMap(final Object bean, final String... properties) {
int mapSize = 16; int mapSize = 16;
Editor<String> keyEditor = null; Editor<MutableEntry<String, Object>> editor = null;
if (ArrayUtil.isNotEmpty(properties)) { if (ArrayUtil.isNotEmpty(properties)) {
mapSize = properties.length; mapSize = properties.length;
final Set<String> propertiesSet = SetUtil.of(properties); final Set<String> propertiesSet = SetUtil.of(properties);
keyEditor = property -> propertiesSet.contains(property) ? property : null; editor = entry -> {
final String key = entry.getKey();
entry.setKey(propertiesSet.contains(key) ? key : null);
return entry;
};
} }
// 指明了要复制的属性 所以不忽略null值 // 指明了要复制的属性 所以不忽略null值
return beanToMap(bean, new LinkedHashMap<>(mapSize, 1), false, keyEditor); return beanToMap(bean, new LinkedHashMap<>(mapSize, 1), false, editor);
} }
/** /**
@@ -568,7 +573,11 @@ public class BeanUtil {
return null; return null;
} }
return beanToMap(bean, targetMap, ignoreNullValue, key -> isToUnderlineCase ? StrUtil.toUnderlineCase(key) : key); return beanToMap(bean, targetMap, ignoreNullValue, entry -> {
final String key = entry.getKey();
entry.setKey(isToUnderlineCase ? StrUtil.toUnderlineCase(key) : key);
return entry;
});
} }
/** /**
@@ -588,15 +597,16 @@ public class BeanUtil {
* @return Map * @return Map
* @since 4.0.5 * @since 4.0.5
*/ */
public static Map<String, Object> beanToMap(final Object bean, final Map<String, Object> targetMap, final boolean ignoreNullValue, final Editor<String> keyEditor) { public static Map<String, Object> beanToMap(final Object bean, final Map<String, Object> targetMap,
final boolean ignoreNullValue, final Editor<MutableEntry<String, Object>> keyEditor) {
if (null == bean) { if (null == bean) {
return null; return null;
} }
return BeanCopier.create(bean, targetMap, return BeanCopier.create(bean, targetMap,
CopyOptions.create() CopyOptions.of()
.setIgnoreNullValue(ignoreNullValue) .setIgnoreNullValue(ignoreNullValue)
.setFieldNameEditor(keyEditor) .setFieldEditor(keyEditor)
).copy(); ).copy();
} }
@@ -642,7 +652,7 @@ public class BeanUtil {
return null; return null;
} }
final T target = ConstructorUtil.newInstanceIfPossible(tClass); final T target = ConstructorUtil.newInstanceIfPossible(tClass);
copyProperties(source, target, CopyOptions.create().setIgnoreProperties(ignoreProperties)); copyProperties(source, target, CopyOptions.of().setIgnoreProperties(ignoreProperties));
return target; return target;
} }
@@ -655,7 +665,7 @@ public class BeanUtil {
* @param ignoreProperties 不拷贝的的属性列表 * @param ignoreProperties 不拷贝的的属性列表
*/ */
public static void copyProperties(final Object source, final Object target, final String... ignoreProperties) { public static void copyProperties(final Object source, final Object target, final String... ignoreProperties) {
copyProperties(source, target, CopyOptions.create().setIgnoreProperties(ignoreProperties)); copyProperties(source, target, CopyOptions.of().setIgnoreProperties(ignoreProperties));
} }
/** /**
@@ -666,7 +676,7 @@ public class BeanUtil {
* @param ignoreCase 是否忽略大小写 * @param ignoreCase 是否忽略大小写
*/ */
public static void copyProperties(final Object source, final Object target, final boolean ignoreCase) { public static void copyProperties(final Object source, final Object target, final boolean ignoreCase) {
BeanCopier.create(source, target, CopyOptions.create().setIgnoreCase(ignoreCase)).copy(); BeanCopier.create(source, target, CopyOptions.of().setIgnoreCase(ignoreCase)).copy();
} }
/** /**
@@ -681,7 +691,7 @@ public class BeanUtil {
if (null == source || null == target) { if (null == source || null == target) {
return; return;
} }
BeanCopier.create(source, target, ObjUtil.defaultIfNull(copyOptions, CopyOptions::create)).copy(); BeanCopier.create(source, target, ObjUtil.defaultIfNull(copyOptions, CopyOptions::of)).copy();
} }
/** /**
@@ -720,7 +730,7 @@ public class BeanUtil {
* @since 5.6.6 * @since 5.6.6
*/ */
public static <T> List<T> copyToList(final Collection<?> collection, final Class<T> targetType) { public static <T> List<T> copyToList(final Collection<?> collection, final Class<T> targetType) {
return copyToList(collection, targetType, CopyOptions.create()); return copyToList(collection, targetType, CopyOptions.of());
} }
/** /**

View File

@@ -23,6 +23,6 @@ public abstract class AbsCopier<S, T> implements Copier<T> {
public AbsCopier(final S source, final T target, final CopyOptions copyOptions) { public AbsCopier(final S source, final T target, final CopyOptions copyOptions) {
this.source = source; this.source = source;
this.target = target; this.target = target;
this.copyOptions = ObjUtil.defaultIfNull(copyOptions, CopyOptions::create); this.copyOptions = ObjUtil.defaultIfNull(copyOptions, CopyOptions::of);
} }
} }

View File

@@ -3,6 +3,7 @@ package cn.hutool.core.bean.copier;
import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.PropDesc; import cn.hutool.core.bean.PropDesc;
import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.mutable.MutableEntry;
import cn.hutool.core.reflect.TypeUtil; import cn.hutool.core.reflect.TypeUtil;
import java.lang.reflect.Type; import java.lang.reflect.Type;
@@ -53,30 +54,36 @@ public class BeanToBeanCopier<S, T> extends AbsCopier<S, T> {
return; return;
} }
sFieldName = copyOptions.editFieldName(sFieldName); // 检查源对象属性是否过滤属性
Object sValue = sDesc.getValue(this.source);
if (false == copyOptions.testPropertyFilter(sDesc.getField(), sValue)) {
return;
}
// 编辑键值对
final MutableEntry<String, Object> entry = copyOptions.editField(sFieldName, sValue);
if(null == entry){
return;
}
sFieldName = entry.getKey();
// 对key做转换转换后为null的跳过 // 对key做转换转换后为null的跳过
if (null == sFieldName) { if (null == sFieldName) {
return; return;
} }
sValue = entry.getValue();
// 检查目标字段可写性 // 检查目标字段可写性
// 目标字段检查放在键值对编辑之后,因为键可能被编辑修改
final PropDesc tDesc = targetPropDescMap.get(sFieldName); final PropDesc tDesc = targetPropDescMap.get(sFieldName);
if (null == tDesc || false == tDesc.isWritable(this.copyOptions.transientSupport)) { if (null == tDesc || false == tDesc.isWritable(this.copyOptions.transientSupport)) {
// 字段不可写,跳过之 // 字段不可写,跳过之
return; return;
} }
// 检查源对象属性是否过滤属性
Object sValue = sDesc.getValue(this.source);
if (false == copyOptions.testPropertyFilter(sDesc.getField(), sValue)) {
return;
}
// 获取目标字段真实类型并转换源值 // 获取目标字段真实类型并转换源值
final Type fieldType = TypeUtil.getActualType(this.targetType, tDesc.getFieldType()); final Type fieldType = TypeUtil.getActualType(this.targetType, tDesc.getFieldType());
//sValue = Convert.convertWithCheck(fieldType, sValue, null, this.copyOptions.ignoreError); //sValue = Convert.convertWithCheck(fieldType, sValue, null, this.copyOptions.ignoreError);
sValue = this.copyOptions.convertField(fieldType, sValue); sValue = this.copyOptions.convertField(fieldType, sValue);
sValue = copyOptions.editFieldValue(sFieldName, sValue);
// 目标赋值 // 目标赋值
tDesc.setValue(this.target, sValue, copyOptions.ignoreNullValue, copyOptions.ignoreError, copyOptions.override); tDesc.setValue(this.target, sValue, copyOptions.ignoreNullValue, copyOptions.ignoreError, copyOptions.override);

View File

@@ -3,6 +3,7 @@ package cn.hutool.core.bean.copier;
import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.PropDesc; import cn.hutool.core.bean.PropDesc;
import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.mutable.MutableEntry;
import cn.hutool.core.reflect.TypeUtil; import cn.hutool.core.reflect.TypeUtil;
import java.lang.reflect.Type; import java.lang.reflect.Type;
@@ -52,24 +53,29 @@ public class BeanToMapCopier extends AbsCopier<Object, Map> {
return; return;
} }
sFieldName = copyOptions.editFieldName(sFieldName);
// 对key做转换转换后为null的跳过
if (null == sFieldName) {
return;
}
// 检查源对象属性是否过滤属性 // 检查源对象属性是否过滤属性
Object sValue = sDesc.getValue(this.source); Object sValue = sDesc.getValue(this.source);
if (false == copyOptions.testPropertyFilter(sDesc.getField(), sValue)) { if (false == copyOptions.testPropertyFilter(sDesc.getField(), sValue)) {
return; return;
} }
// 编辑键值对
final MutableEntry<String, Object> entry = copyOptions.editField(sFieldName, sValue);
if(null == entry){
return;
}
sFieldName = entry.getKey();
// 对key做转换转换后为null的跳过
if (null == sFieldName) {
return;
}
sValue = entry.getValue();
// 获取目标值真实类型并转换源值 // 获取目标值真实类型并转换源值
final Type[] typeArguments = TypeUtil.getTypeArguments(this.targetType); final Type[] typeArguments = TypeUtil.getTypeArguments(this.targetType);
if(null != typeArguments){ if(null != typeArguments){
//sValue = Convert.convertWithCheck(typeArguments[1], sValue, null, this.copyOptions.ignoreError); //sValue = Convert.convertWithCheck(typeArguments[1], sValue, null, this.copyOptions.ignoreError);
sValue = this.copyOptions.convertField(typeArguments[1], sValue); sValue = this.copyOptions.convertField(typeArguments[1], sValue);
sValue = copyOptions.editFieldValue(sFieldName, sValue);
} }
// 目标赋值 // 目标赋值

View File

@@ -5,6 +5,7 @@ import cn.hutool.core.convert.Converter;
import cn.hutool.core.lang.func.Editor; import cn.hutool.core.lang.func.Editor;
import cn.hutool.core.lang.func.Func1; import cn.hutool.core.lang.func.Func1;
import cn.hutool.core.lang.func.LambdaUtil; import cn.hutool.core.lang.func.LambdaUtil;
import cn.hutool.core.lang.mutable.MutableEntry;
import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.ArrayUtil;
import java.io.Serializable; import java.io.Serializable;
@@ -12,7 +13,6 @@ import java.lang.reflect.Field;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.BiPredicate; import java.util.function.BiPredicate;
/** /**
@@ -36,11 +36,6 @@ public class CopyOptions implements Serializable {
* 是否忽略空值当源对象的值为null时true: 忽略而不注入此值false: 注入null * 是否忽略空值当源对象的值为null时true: 忽略而不注入此值false: 注入null
*/ */
protected boolean ignoreNullValue; protected boolean ignoreNullValue;
/**
* 属性过滤器,断言通过的属性才会被复制<br>
* 断言参数中Field为源对象的字段对象,如果源对象为Map使用目标对象Object为源对象的对应值
*/
private BiPredicate<Field, Object> propertiesFilter;
/** /**
* 是否忽略字段注入错误 * 是否忽略字段注入错误
*/ */
@@ -50,14 +45,16 @@ public class CopyOptions implements Serializable {
*/ */
protected boolean ignoreCase; protected boolean ignoreCase;
/** /**
* 字段属性编辑器,用于自定义属性转换规则,例如驼峰转下划线等<br> * 属性过滤器,断言通过的属性才会被复制<br>
* 规则为,{@link Editor#edit(Object)}属性为源对象的字段名称或key返回值为目标对象的字段名称或key * 断言参数中Field为源对象的字段对象,如果源对象为Map使用目标对象Object为源对象的对应值
*/ */
private Editor<String> fieldNameEditor; private BiPredicate<Field, Object> propertiesFilter;
/** /**
* 字段属性值编辑器,用于自定义属性值转换规则例如null转""等 * 字段属性名和属性值编辑器,用于自定义属性转换规则(例如驼峰转下划线等),自定义属性值转换规则例如null转""等
*/ */
protected BiFunction<String, Object, Object> fieldValueEditor; protected Editor<MutableEntry<String, Object>> fieldEditor;
/** /**
* 是否支持transient关键字修饰和@Transient注解如果支持被修饰的字段或方法对应的字段将被忽略。 * 是否支持transient关键字修饰和@Transient注解如果支持被修饰的字段或方法对应的字段将被忽略。
*/ */
@@ -80,7 +77,7 @@ public class CopyOptions implements Serializable {
* *
* @return 拷贝选项 * @return 拷贝选项
*/ */
public static CopyOptions create() { public static CopyOptions of() {
return new CopyOptions(); return new CopyOptions();
} }
@@ -92,7 +89,7 @@ public class CopyOptions implements Serializable {
* @param ignoreProperties 忽略的属性列表,设置一个属性列表,不拷贝这些属性值 * @param ignoreProperties 忽略的属性列表,设置一个属性列表,不拷贝这些属性值
* @return 拷贝选项 * @return 拷贝选项
*/ */
public static CopyOptions create(final Class<?> editable, final boolean ignoreNullValue, final String... ignoreProperties) { public static CopyOptions of(final Class<?> editable, final boolean ignoreNullValue, final String... ignoreProperties) {
return new CopyOptions(editable, ignoreNullValue, ignoreProperties); return new CopyOptions(editable, ignoreNullValue, ignoreProperties);
} }
//endregion //endregion
@@ -235,7 +232,11 @@ public class CopyOptions implements Serializable {
* @return CopyOptions * @return CopyOptions
*/ */
public CopyOptions setFieldMapping(final Map<String, String> fieldMapping) { public CopyOptions setFieldMapping(final Map<String, String> fieldMapping) {
return setFieldNameEditor((key -> fieldMapping.getOrDefault(key, key))); return setFieldEditor(entry -> {
final String key = entry.getKey();
entry.setKey(fieldMapping.getOrDefault(key, key));
return entry;
});
} }
/** /**
@@ -243,24 +244,12 @@ public class CopyOptions implements Serializable {
* 此转换器只针对源端的字段做转换,请确认转换后与目标端字段一致<br> * 此转换器只针对源端的字段做转换,请确认转换后与目标端字段一致<br>
* 当转换后的字段名为null时忽略这个字段 * 当转换后的字段名为null时忽略这个字段
* *
* @param fieldNameEditor 字段属性编辑器,用于自定义属性转换规则,例如驼峰转下划线等 * @param editor 字段属性编辑器,用于自定义属性转换规则,例如驼峰转下划线等
* @return CopyOptions * @return CopyOptions
* @since 5.4.2 * @since 5.4.2
*/ */
public CopyOptions setFieldNameEditor(final Editor<String> fieldNameEditor) { public CopyOptions setFieldEditor(final Editor<MutableEntry<String, Object>> editor) {
this.fieldNameEditor = fieldNameEditor; this.fieldEditor = editor;
return this;
}
/**
* 设置字段属性值编辑器用于自定义属性值转换规则例如null转""等<br>
*
* @param fieldValueEditor 字段属性值编辑器用于自定义属性值转换规则例如null转""等
* @return CopyOptions
* @since 5.7.15
*/
public CopyOptions setFieldValueEditor(final BiFunction<String, Object, Object> fieldValueEditor) {
this.fieldValueEditor = fieldValueEditor;
return this; return this;
} }
@@ -272,9 +261,10 @@ public class CopyOptions implements Serializable {
* @return 编辑后的字段值 * @return 编辑后的字段值
* @since 5.7.15 * @since 5.7.15
*/ */
protected Object editFieldValue(final String fieldName, final Object fieldValue) { protected MutableEntry<String, Object> editField(final String fieldName, final Object fieldValue) {
return (null != this.fieldValueEditor) ? final MutableEntry<String, Object> entry = new MutableEntry<>(fieldName, fieldValue);
this.fieldValueEditor.apply(fieldName, fieldValue) : fieldValue; return (null != this.fieldEditor) ?
this.fieldEditor.edit(entry) : entry;
} }
/** /**
@@ -327,17 +317,6 @@ public class CopyOptions implements Serializable {
this.converter.convert(targetType, fieldValue) : fieldValue; this.converter.convert(targetType, fieldValue) : fieldValue;
} }
/**
* 转换字段名为编辑后的字段名
*
* @param fieldName 字段名
* @return 编辑后的字段名
* @since 5.4.2
*/
protected String editFieldName(final String fieldName) {
return (null != this.fieldNameEditor) ? this.fieldNameEditor.edit(fieldName) : fieldName;
}
/** /**
* 测试是否保留字段,{@code true}保留,{@code false}不保留 * 测试是否保留字段,{@code true}保留,{@code false}不保留
* *

View File

@@ -3,6 +3,7 @@ package cn.hutool.core.bean.copier;
import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.PropDesc; import cn.hutool.core.bean.PropDesc;
import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.mutable.MutableEntry;
import cn.hutool.core.map.CaseInsensitiveMap; import cn.hutool.core.map.CaseInsensitiveMap;
import cn.hutool.core.map.MapWrapper; import cn.hutool.core.map.MapWrapper;
import cn.hutool.core.text.StrUtil; import cn.hutool.core.text.StrUtil;
@@ -61,30 +62,35 @@ public class MapToBeanCopier<T> extends AbsCopier<Map<?, ?>, T> {
if (null == sKey) { if (null == sKey) {
return; return;
} }
String sKeyStr = copyOptions.editFieldName(sKey.toString());
// 编辑键值对
final MutableEntry<String, Object> entry = copyOptions.editField(sKey.toString(), sValue);
if(null == entry){
return;
}
String sFieldName = entry.getKey();
// 对key做转换转换后为null的跳过 // 对key做转换转换后为null的跳过
if (null == sKeyStr) { if (null == sFieldName) {
return; return;
} }
// 检查目标字段可写性 // 检查目标字段可写性
final PropDesc tDesc = findPropDesc(targetPropDescMap, sKeyStr); // 目标字段检查放在键值对编辑之后,因为键可能被编辑修改
final PropDesc tDesc = findPropDesc(targetPropDescMap, sFieldName);
if (null == tDesc || false == tDesc.isWritable(this.copyOptions.transientSupport)) { if (null == tDesc || false == tDesc.isWritable(this.copyOptions.transientSupport)) {
// 字段不可写,跳过之 // 字段不可写,跳过之
return; return;
} }
sKeyStr = tDesc.getFieldName();
Object newValue = entry.getValue();
// 检查目标是否过滤属性 // 检查目标是否过滤属性
if (false == copyOptions.testPropertyFilter(tDesc.getField(), sValue)) { if (false == copyOptions.testPropertyFilter(tDesc.getField(), newValue)) {
return; return;
} }
// 获取目标字段真实类型并转换源值 // 获取目标字段真实类型并转换源值
final Type fieldType = TypeUtil.getActualType(this.targetType, tDesc.getFieldType()); final Type fieldType = TypeUtil.getActualType(this.targetType, tDesc.getFieldType());
//Object newValue = Convert.convertWithCheck(fieldType, sValue, null, this.copyOptions.ignoreError); newValue = this.copyOptions.convertField(fieldType, newValue);
Object newValue = this.copyOptions.convertField(fieldType, sValue);
newValue = copyOptions.editFieldValue(sKeyStr, newValue);
// 目标赋值 // 目标赋值
tDesc.setValue(this.target, newValue, copyOptions.ignoreNullValue, copyOptions.ignoreError, copyOptions.override); tDesc.setValue(this.target, newValue, copyOptions.ignoreNullValue, copyOptions.ignoreError, copyOptions.override);

View File

@@ -1,5 +1,6 @@
package cn.hutool.core.bean.copier; package cn.hutool.core.bean.copier;
import cn.hutool.core.lang.mutable.MutableEntry;
import cn.hutool.core.reflect.TypeUtil; import cn.hutool.core.reflect.TypeUtil;
import java.lang.reflect.Type; import java.lang.reflect.Type;
@@ -37,13 +38,20 @@ public class MapToMapCopier extends AbsCopier<Map, Map> {
if (null == sKey) { if (null == sKey) {
return; return;
} }
final String sKeyStr = copyOptions.editFieldName(sKey.toString());
// 对key做转换转换后为null的跳过 // 编辑键值
if (null == sKeyStr) { final MutableEntry<String, Object> entry = copyOptions.editField(sKey.toString(), sValue);
if(null == entry){
return; return;
} }
sKey = entry.getKey();
// 对key做转换转换后为null的跳过
if (null == sKey) {
return;
}
sValue = entry.getValue();
final Object targetValue = target.get(sKeyStr); final Object targetValue = target.get(sKey);
// 非覆盖模式下,如果目标值存在,则跳过 // 非覆盖模式下,如果目标值存在,则跳过
if (false == copyOptions.override && null != targetValue) { if (false == copyOptions.override && null != targetValue) {
return; return;
@@ -52,13 +60,11 @@ public class MapToMapCopier extends AbsCopier<Map, Map> {
// 获取目标值真实类型并转换源值 // 获取目标值真实类型并转换源值
final Type[] typeArguments = TypeUtil.getTypeArguments(this.targetType); final Type[] typeArguments = TypeUtil.getTypeArguments(this.targetType);
if(null != typeArguments){ if(null != typeArguments){
//sValue = Convert.convertWithCheck(typeArguments[1], sValue, null, this.copyOptions.ignoreError);
sValue = this.copyOptions.convertField(typeArguments[1], sValue); sValue = this.copyOptions.convertField(typeArguments[1], sValue);
sValue = copyOptions.editFieldValue(sKeyStr, sValue);
} }
// 目标赋值 // 目标赋值
target.put(sKeyStr, sValue); target.put(sKey, sValue);
}); });
return this.target; return this.target;
} }

View File

@@ -3,6 +3,7 @@ package cn.hutool.core.bean.copier;
import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.PropDesc; import cn.hutool.core.bean.PropDesc;
import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.mutable.MutableEntry;
import cn.hutool.core.reflect.TypeUtil; import cn.hutool.core.reflect.TypeUtil;
import java.lang.reflect.Type; import java.lang.reflect.Type;
@@ -63,13 +64,23 @@ public class ValueProviderToBeanCopier<T> extends AbsCopier<ValueProvider<String
// 获取目标字段真实类型 // 获取目标字段真实类型
final Type fieldType = TypeUtil.getActualType(this.targetType ,tDesc.getFieldType()); final Type fieldType = TypeUtil.getActualType(this.targetType ,tDesc.getFieldType());
Object sValue = source.value(tFieldName, fieldType);
// 编辑键值对
final MutableEntry<String, Object> entry = copyOptions.editField(tFieldName, sValue);
if(null == entry){
return;
}
tFieldName = entry.getKey();
// 对key做转换转换后为null的跳过
if (null == tFieldName) {
return;
}
sValue = entry.getValue();
// 检查目标对象属性是否过滤属性 // 检查目标对象属性是否过滤属性
Object sValue = source.value(tFieldName, fieldType);
if (false == copyOptions.testPropertyFilter(tDesc.getField(), sValue)) { if (false == copyOptions.testPropertyFilter(tDesc.getField(), sValue)) {
return; return;
} }
sValue = copyOptions.editFieldValue(tFieldName, sValue);
// 目标赋值 // 目标赋值
tDesc.setValue(this.target, sValue, copyOptions.ignoreNullValue, copyOptions.ignoreError, copyOptions.override); tDesc.setValue(this.target, sValue, copyOptions.ignoreNullValue, copyOptions.ignoreError, copyOptions.override);

View File

@@ -38,7 +38,7 @@ public class BeanConverter implements Converter, Serializable {
* 构造 * 构造
*/ */
public BeanConverter() { public BeanConverter() {
this(CopyOptions.create().setIgnoreError(true)); this(CopyOptions.of().setIgnoreError(true));
} }
/** /**

View File

@@ -247,7 +247,7 @@ public class Dict extends LinkedHashMap<String, Object> implements BasicTypeGett
* @return vo * @return vo
*/ */
public <T> T toBeanIgnoreCase(final Class<T> clazz) { public <T> T toBeanIgnoreCase(final Class<T> clazz) {
return BeanUtil.toBean(this, clazz, CopyOptions.create().setIgnoreCase(true)); return BeanUtil.toBean(this, clazz, CopyOptions.of().setIgnoreCase(true));
} }
/** /**

View File

@@ -15,15 +15,17 @@ public class BeanCopyMappingTest {
*/ */
@Test @Test
public void copyPropertiesTest() { public void copyPropertiesTest() {
final CopyOptions copyOptions = CopyOptions.create() final CopyOptions copyOptions = CopyOptions.of()
.setFieldMapping(MapUtil.of("car", "carNo")); .setFieldMapping(MapUtil.of("car", "carNo"));
final B b = B.builder().car("12312312").build(); final B b = B.builder().car("12312312").build();
final A a = A.builder().build(); final A a = A.builder().build();
final C c = C.builder().build(); final C c = C.builder().build();
BeanUtil.copyProperties(b, a, copyOptions); BeanUtil.copyProperties(b, a, copyOptions);
BeanUtil.copyProperties(a, c); BeanUtil.copyProperties(a, c);
Assert.assertEquals("12312312", a.getCarNo());
Assert.assertEquals("12312312", c.getCarNo()); Assert.assertEquals("12312312", c.getCarNo());
} }

View File

@@ -71,7 +71,7 @@ public class BeanUtilTest {
return true; return true;
} }
}, CopyOptions.create()); }, CopyOptions.of());
Assert.assertEquals("张三", person.getName()); Assert.assertEquals("张三", person.getName());
Assert.assertEquals(18, person.getAge()); Assert.assertEquals(18, person.getAge());
@@ -116,7 +116,7 @@ public class BeanUtilTest {
// 错误的类型,此处忽略 // 错误的类型,此处忽略
map.put("age", "aaaaaa"); map.put("age", "aaaaaa");
final Person person = BeanUtil.toBean(map, Person.class, CopyOptions.create().setIgnoreError(true)); final Person person = BeanUtil.toBean(map, Person.class, CopyOptions.of().setIgnoreError(true));
Assert.assertEquals("Joe", person.getName()); Assert.assertEquals("Joe", person.getName());
// 错误的类型不copy这个字段使用对象创建的默认值 // 错误的类型不copy这个字段使用对象创建的默认值
Assert.assertEquals(0, person.getAge()); Assert.assertEquals(0, person.getAge());
@@ -128,7 +128,7 @@ public class BeanUtilTest {
map.put("Name", "Joe"); map.put("Name", "Joe");
map.put("aGe", 12); map.put("aGe", 12);
final Person person = BeanUtil.toBean(map, Person.class, CopyOptions.create().setIgnoreCase(true)); final Person person = BeanUtil.toBean(map, Person.class, CopyOptions.of().setIgnoreCase(true));
Assert.assertEquals("Joe", person.getName()); Assert.assertEquals("Joe", person.getName());
Assert.assertEquals(12, person.getAge()); Assert.assertEquals(12, person.getAge());
} }
@@ -144,7 +144,7 @@ public class BeanUtilTest {
mapping.put("a_name", "name"); mapping.put("a_name", "name");
mapping.put("b_age", "age"); mapping.put("b_age", "age");
final Person person = BeanUtil.toBean(map, Person.class, CopyOptions.create().setFieldMapping(mapping)); final Person person = BeanUtil.toBean(map, Person.class, CopyOptions.of().setFieldMapping(mapping));
Assert.assertEquals("Joe", person.getName()); Assert.assertEquals("Joe", person.getName());
Assert.assertEquals(12, person.getAge()); Assert.assertEquals(12, person.getAge());
} }
@@ -159,7 +159,7 @@ public class BeanUtilTest {
map.put("age", 12); map.put("age", 12);
// 非空构造也可以实例化成功 // 非空构造也可以实例化成功
final Person2 person = BeanUtil.toBean(map, Person2.class, CopyOptions.create()); final Person2 person = BeanUtil.toBean(map, Person2.class, CopyOptions.of());
Assert.assertEquals("Joe", person.name); Assert.assertEquals("Joe", person.name);
Assert.assertEquals(12, person.age); Assert.assertEquals(12, person.age);
} }
@@ -229,7 +229,10 @@ public class BeanUtilTest {
person.setSubName("sub名字"); person.setSubName("sub名字");
final Map<String, Object> map = BeanUtil.beanToMap(person, new LinkedHashMap<>(), final Map<String, Object> map = BeanUtil.beanToMap(person, new LinkedHashMap<>(),
CopyOptions.create().setFieldValueEditor((key, value) -> key + "_" + value)); CopyOptions.of().setFieldEditor((entry) -> {
entry.setValue(entry.getKey() + "_" + entry.getValue());
return entry;
}));
Assert.assertEquals("subName_sub名字", map.get("subName")); Assert.assertEquals("subName_sub名字", map.get("subName"));
} }
@@ -387,7 +390,7 @@ public class BeanUtilTest {
p2.setName("oldName"); p2.setName("oldName");
// null值不覆盖目标属性 // null值不覆盖目标属性
BeanUtil.copyProperties(p1, p2, CopyOptions.create().ignoreNullValue()); BeanUtil.copyProperties(p1, p2, CopyOptions.of().ignoreNullValue());
Assert.assertEquals("oldName", p2.getName()); Assert.assertEquals("oldName", p2.getName());
// null覆盖目标属性 // null覆盖目标属性
@@ -578,7 +581,7 @@ public class BeanUtilTest {
info.setBookID("0"); info.setBookID("0");
info.setCode(""); info.setCode("");
final Food newFood = new Food(); final Food newFood = new Food();
final CopyOptions copyOptions = CopyOptions.create().setPropertiesFilter((f, v) -> !(v instanceof CharSequence) || StrUtil.isNotBlank(v.toString())); final CopyOptions copyOptions = CopyOptions.of().setPropertiesFilter((f, v) -> !(v instanceof CharSequence) || StrUtil.isNotBlank(v.toString()));
BeanUtil.copyProperties(info, newFood, copyOptions); BeanUtil.copyProperties(info, newFood, copyOptions);
Assert.assertEquals(info.getBookID(), newFood.getBookID()); Assert.assertEquals(info.getBookID(), newFood.getBookID());
Assert.assertNull(newFood.getCode()); Assert.assertNull(newFood.getCode());
@@ -592,7 +595,7 @@ public class BeanUtilTest {
o.setAge(123); o.setAge(123);
o.setOpenid("asd"); o.setOpenid("asd");
@SuppressWarnings("unchecked") final CopyOptions copyOptions = CopyOptions.create().setIgnoreProperties(Person::getAge,Person::getOpenid); @SuppressWarnings("unchecked") final CopyOptions copyOptions = CopyOptions.of().setIgnoreProperties(Person::getAge,Person::getOpenid);
final Person n = new Person(); final Person n = new Person();
BeanUtil.copyProperties(o, n, copyOptions); BeanUtil.copyProperties(o, n, copyOptions);
@@ -680,7 +683,12 @@ public class BeanUtilTest {
a, a,
new LinkedHashMap<>(), new LinkedHashMap<>(),
false, false,
key -> Arrays.asList("id", "name", "code", "sortOrder").contains(key) ? key : null); entry -> {
if(false == Arrays.asList("id", "name", "code", "sortOrder").contains(entry.getKey())){
entry.setKey(null);
}
return entry;
});
Assert.assertFalse(f.containsKey(null)); Assert.assertFalse(f.containsKey(null));
} }
@@ -749,10 +757,13 @@ public class BeanUtilTest {
childVo1.setChild_father_name("张无忌"); childVo1.setChild_father_name("张无忌");
childVo1.setChild_mother_name("赵敏敏"); childVo1.setChild_mother_name("赵敏敏");
final CopyOptions copyOptions = CopyOptions.create(). final CopyOptions copyOptions = CopyOptions.of().
//setIgnoreNullValue(true). //setIgnoreNullValue(true).
//setIgnoreCase(false). //setIgnoreCase(false).
setFieldNameEditor(StrUtil::toCamelCase); setFieldEditor(entry->{
entry.setKey(StrUtil.toCamelCase(entry.getKey()));
return entry;
});
final ChildVo2 childVo2 = new ChildVo2(); final ChildVo2 childVo2 = new ChildVo2();
BeanUtil.copyProperties(childVo1, childVo2, copyOptions); BeanUtil.copyProperties(childVo1, childVo2, copyOptions);
@@ -783,7 +794,7 @@ public class BeanUtilTest {
public void issueI41WKPTest(){ public void issueI41WKPTest(){
final Test1 t1 = new Test1().setStrList(ListUtil.of("list")); final Test1 t1 = new Test1().setStrList(ListUtil.of("list"));
final Test2 t2_hu = new Test2(); final Test2 t2_hu = new Test2();
BeanUtil.copyProperties(t1, t2_hu, CopyOptions.create().setIgnoreError(true)); BeanUtil.copyProperties(t1, t2_hu, CopyOptions.of().setIgnoreError(true));
Assert.assertNull(t2_hu.getStrList()); Assert.assertNull(t2_hu.getStrList());
} }

View File

@@ -33,7 +33,7 @@ public class Issue1687Test {
sysUserFb.setCustomerId("456"); sysUserFb.setCustomerId("456");
// 补救别名错位 // 补救别名错位
final CopyOptions copyOptions = CopyOptions.create().setFieldMapping( final CopyOptions copyOptions = CopyOptions.of().setFieldMapping(
MapUtil.builder("depart", "depId").build() MapUtil.builder("depart", "depId").build()
); );
final SysUser sysUser = BeanUtil.toBean(sysUserFb, SysUser.class, copyOptions); final SysUser sysUser = BeanUtil.toBean(sysUserFb, SysUser.class, copyOptions);

View File

@@ -22,7 +22,10 @@ public class Issue2202Test {
headerMap.put("wechatpay-timestamp", "timestamp"); headerMap.put("wechatpay-timestamp", "timestamp");
headerMap.put("wechatpay-signature", "signature"); headerMap.put("wechatpay-signature", "signature");
final ResponseSignVerifyParams case1 = BeanUtil.toBean(headerMap, ResponseSignVerifyParams.class, final ResponseSignVerifyParams case1 = BeanUtil.toBean(headerMap, ResponseSignVerifyParams.class,
CopyOptions.create().setFieldNameEditor(field -> NamingCase.toCamelCase(field, '-'))); CopyOptions.of().setFieldEditor(entry -> {
entry.setKey(NamingCase.toCamelCase(entry.getKey(), '-'));
return entry;
}));
Assert.assertEquals("serial", case1.getWechatpaySerial()); Assert.assertEquals("serial", case1.getWechatpaySerial());
Assert.assertEquals("nonce", case1.getWechatpayNonce()); Assert.assertEquals("nonce", case1.getWechatpayNonce());

View File

@@ -12,13 +12,13 @@ public class BeanCopierTest {
public void beanToMapIgnoreNullTest() { public void beanToMapIgnoreNullTest() {
final A a = new A(); final A a = new A();
HashMap<Object, Object> map = BeanCopier.create(a, new HashMap<>(), CopyOptions.create()).copy(); HashMap<Object, Object> map = BeanCopier.create(a, new HashMap<>(), CopyOptions.of()).copy();
Assert.assertEquals(1, map.size()); Assert.assertEquals(1, map.size());
Assert.assertTrue(map.containsKey("value")); Assert.assertTrue(map.containsKey("value"));
Assert.assertNull(map.get("value")); Assert.assertNull(map.get("value"));
// 忽略null的情况下空字段不写入map // 忽略null的情况下空字段不写入map
map = BeanCopier.create(a, new HashMap<>(), CopyOptions.create().ignoreNullValue()).copy(); map = BeanCopier.create(a, new HashMap<>(), CopyOptions.of().ignoreNullValue()).copy();
Assert.assertFalse(map.containsKey("value")); Assert.assertFalse(map.containsKey("value"));
Assert.assertEquals(0, map.size()); Assert.assertEquals(0, map.size());
} }
@@ -33,7 +33,7 @@ public class BeanCopierTest {
final B b = new B(); final B b = new B();
b.setValue("abc"); b.setValue("abc");
final BeanCopier<B> copier = BeanCopier.create(a, b, CopyOptions.create().setOverride(false)); final BeanCopier<B> copier = BeanCopier.create(a, b, CopyOptions.of().setOverride(false));
copier.copy(); copier.copy();
Assert.assertEquals("abc", b.getValue()); Assert.assertEquals("abc", b.getValue());
@@ -49,7 +49,7 @@ public class BeanCopierTest {
final B b = new B(); final B b = new B();
b.setValue("abc"); b.setValue("abc");
final BeanCopier<B> copier = BeanCopier.create(a, b, CopyOptions.create()); final BeanCopier<B> copier = BeanCopier.create(a, b, CopyOptions.of());
copier.copy(); copier.copy();
Assert.assertEquals("123", b.getValue()); Assert.assertEquals("123", b.getValue());

View File

@@ -169,7 +169,7 @@ public class JakartaServletUtil {
* @return Bean * @return Bean
*/ */
public static <T> T fillBean(final ServletRequest request, final T bean, final boolean isIgnoreError) { public static <T> T fillBean(final ServletRequest request, final T bean, final boolean isIgnoreError) {
return fillBean(request, bean, CopyOptions.create().setIgnoreError(isIgnoreError)); return fillBean(request, bean, CopyOptions.of().setIgnoreError(isIgnoreError));
} }
/** /**

View File

@@ -169,7 +169,7 @@ public class ServletUtil {
* @return Bean * @return Bean
*/ */
public static <T> T fillBean(final ServletRequest request, final T bean, final boolean isIgnoreError) { public static <T> T fillBean(final ServletRequest request, final T bean, final boolean isIgnoreError) {
return fillBean(request, bean, CopyOptions.create().setIgnoreError(isIgnoreError)); return fillBean(request, bean, CopyOptions.of().setIgnoreError(isIgnoreError));
} }
/** /**

View File

@@ -184,7 +184,7 @@ public final class InternalJSONUtil {
* @since 5.8.0 * @since 5.8.0
*/ */
static CopyOptions toCopyOptions(final JSONConfig config) { static CopyOptions toCopyOptions(final JSONConfig config) {
return CopyOptions.create() return CopyOptions.of()
.setIgnoreCase(config.isIgnoreCase()) .setIgnoreCase(config.isIgnoreCase())
.setIgnoreError(config.isIgnoreError()) .setIgnoreError(config.isIgnoreError())
.setIgnoreNullValue(config.isIgnoreNullValue()) .setIgnoreNullValue(config.isIgnoreNullValue())

View File

@@ -1,6 +1,7 @@
package cn.hutool.json; package cn.hutool.json;
import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.collection.iter.ArrayIter; import cn.hutool.core.collection.iter.ArrayIter;
import cn.hutool.core.convert.Convert; import cn.hutool.core.convert.Convert;
import cn.hutool.core.io.IoUtil; import cn.hutool.core.io.IoUtil;
@@ -110,8 +111,7 @@ public class ObjectMapper {
mapFromResourceBundle((ResourceBundle) source, jsonObject, filter); mapFromResourceBundle((ResourceBundle) source, jsonObject, filter);
} else if (BeanUtil.isReadableBean(source.getClass())) { } else if (BeanUtil.isReadableBean(source.getClass())) {
// 普通Bean // 普通Bean
// TODO 过滤器对Bean无效需补充。 mapFromBean(source, jsonObject, filter);
mapFromBean(source, jsonObject);
} else { } else {
// 不支持对象类型转换为JSONObject // 不支持对象类型转换为JSONObject
throw new JSONException("Unsupported type [{}] to JSONObject!", source.getClass()); throw new JSONException("Unsupported type [{}] to JSONObject!", source.getClass());
@@ -248,7 +248,9 @@ public class ObjectMapper {
* @param bean Bean对象 * @param bean Bean对象
* @param jsonObject {@link JSONObject} * @param jsonObject {@link JSONObject}
*/ */
private static void mapFromBean(final Object bean, final JSONObject jsonObject) { private static void mapFromBean(final Object bean, final JSONObject jsonObject, final Filter<MutableEntry<String, Object>> filter) {
BeanUtil.beanToMap(bean, jsonObject, InternalJSONUtil.toCopyOptions(jsonObject.getConfig())); final CopyOptions copyOptions = InternalJSONUtil.toCopyOptions(jsonObject.getConfig());
copyOptions.setFieldEditor((entry -> filter.accept(entry) ? entry : null));
BeanUtil.beanToMap(bean, jsonObject, copyOptions);
} }
} }

View File

@@ -107,7 +107,7 @@ public final class CsvRow implements List<String> {
* @since 5.3.6 * @since 5.3.6
*/ */
public <T> T toBean(final Class<T> clazz){ public <T> T toBean(final Class<T> clazz){
return BeanUtil.toBean(getFieldMap(), clazz, CopyOptions.create().setIgnoreError(true)); return BeanUtil.toBean(getFieldMap(), clazz, CopyOptions.of().setIgnoreError(true));
} }
/** /**

View File

@@ -42,7 +42,7 @@ public class BeanSheetReader<T> implements SheetReader<List<T>> {
} }
final List<T> beanList = new ArrayList<>(mapList.size()); final List<T> beanList = new ArrayList<>(mapList.size());
final CopyOptions copyOptions = CopyOptions.create().setIgnoreError(true); final CopyOptions copyOptions = CopyOptions.of().setIgnoreError(true);
for (final Map<String, Object> map : mapList) { for (final Map<String, Object> map : mapList) {
beanList.add(BeanUtil.toBean(map, this.beanClass, copyOptions)); beanList.add(BeanUtil.toBean(map, this.beanClass, copyOptions));
} }

View File

@@ -297,7 +297,7 @@ public abstract class AbsSetting implements OptNullBasicTypeFromStringGetter<Str
public boolean containsKey(final String key) { public boolean containsKey(final String key) {
return null != getByGroup(key, group); return null != getByGroup(key, group);
} }
}, CopyOptions.create()); }, CopyOptions.of());
} }
/** /**