diff --git a/CHANGELOG.md b/CHANGELOG.md index 28c91769b..ab6e618d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ * 【core 】 TypeUtil增加getActualType,增加ActualTypeMapperPool类(issue#I1TBWH@Gitee) * 【extra 】 QRConfig中添加qrVersion属性(pr#1068@Github) * 【core 】 ArrayUtil增加equals方法 +* 【core 】 BeanDesc增加方法 ### Bug修复 * 【core 】 重新整理农历节假日,解决一个pr过来的玩笑导致的问题 diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationUtil.java b/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationUtil.java index 773aca748..ad07b7f73 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationUtil.java @@ -61,6 +61,18 @@ public class AnnotationUtil { return (null == annotationEle) ? null : toCombination(annotationEle).getAnnotation(annotationType); } + /** + * 检查是否包含指定注解指定注解 + * + * @param annotationEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission + * @param annotationType 注解类型 + * @return 是否包含指定注解 + * @since 5.4.2 + */ + public static boolean hasAnnotation(AnnotatedElement annotationEle, Class annotationType) { + return null != getAnnotation(annotationEle, annotationType); + } + /** * 获取指定注解默认值
* 如果无指定的属性方法返回null diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/Ignore.java b/hutool-core/src/main/java/cn/hutool/core/annotation/Ignore.java new file mode 100644 index 000000000..5c9b05aff --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/Ignore.java @@ -0,0 +1,20 @@ +package cn.hutool.core.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 忽略注解,使用此注解的字段等会被忽略,主要用于Bean拷贝、Bean转Map等 + * + * @author Looly + * @since 5.4.2 + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER}) +public @interface Ignore { + +} diff --git a/hutool-core/src/main/java/cn/hutool/core/bean/BeanDesc.java b/hutool-core/src/main/java/cn/hutool/core/bean/BeanDesc.java index 62fc4d268..74d1e761e 100644 --- a/hutool-core/src/main/java/cn/hutool/core/bean/BeanDesc.java +++ b/hutool-core/src/main/java/cn/hutool/core/bean/BeanDesc.java @@ -1,6 +1,8 @@ package cn.hutool.core.bean; import cn.hutool.core.annotation.AnnotationUtil; +import cn.hutool.core.annotation.Ignore; +import cn.hutool.core.convert.Convert; import cn.hutool.core.lang.Assert; import cn.hutool.core.map.CaseInsensitiveMap; import cn.hutool.core.util.BooleanUtil; @@ -439,7 +441,7 @@ public class BeanDesc implements Serializable { } /** - * 获取字段值
+ * 获取属性值
* 首先调用字段对应的Getter方法获取值,如果Getter方法不存在,则判断字段如果为public,则直接获取字段值 * * @param bean Bean对象 @@ -455,12 +457,42 @@ public class BeanDesc implements Serializable { return null; } + /** + * 获取属性值,自动转换属性值类型
+ * 首先调用字段对应的Getter方法获取值,如果Getter方法不存在,则判断字段如果为public,则直接获取字段值 + * + * @param bean Bean对象 + * @param valueType 返回属性值类型,null表示不转换 + * @param ignoreError 是否忽略错误,包括转换错误和注入错误 + * @return this + * @since 5.4.2 + */ + public Object getValueWithConvert(Object bean, Type valueType, boolean ignoreError) { + Object result = null; + try { + result = getValue(bean); + } catch (Exception e) { + if (false == ignoreError) { + throw new BeanException(e, "Get value of [{}] error!", getFieldName()); + } + } + + if (null != result && null != valueType) { + // 尝试将结果转换为目标类型,如果转换失败,返回原类型。 + final Object convertValue = Convert.convertWithCheck(valueType, result, null, ignoreError); + if (null != convertValue) { + result = convertValue; + } + } + return result; + } + /** * 设置Bean的字段值
* 首先调用字段对应的Setter方法,如果Setter方法不存在,则判断字段如果为public,则直接赋值字段值 * * @param bean Bean对象 - * @param value 值 + * @param value 值,必须与字段值类型匹配 * @return this * @since 4.0.5 */ @@ -473,6 +505,44 @@ public class BeanDesc implements Serializable { return this; } + /** + * 设置属性值,可以自动转换字段类型为目标类型 + * + * @param bean Bean对象 + * @param value 属性值,可以为任意类型 + * @param ignoreNull 是否忽略{@code null}值,true表示忽略 + * @param ignoreError 是否忽略错误,包括转换错误和注入错误 + * @return this + * @since 5.4.2 + */ + public PropDesc setValueWithConvert(Object bean, Object value, boolean ignoreNull, boolean ignoreError) { + if (ignoreNull && null == value) { + return this; + } + + // 当类型不匹配的时候,执行默认转换 + if (null != value) { + final Class propClass = getFieldClass(); + if (false == propClass.isInstance(value)) { + value = Convert.convertWithCheck(propClass, value, null, ignoreError); + } + } + + // 属性赋值 + if (null != value || false == ignoreNull) { + try { + this.setValue(bean, value); + } catch (Exception e) { + if (false == ignoreError) { + throw new BeanException(e, "Set value of [{}] error!", getFieldName()); + } + // 忽略注入失败 + } + } + + return this; + } + /** * 字段和Getter方法是否为Transient关键字修饰的 * @@ -488,13 +558,43 @@ public class BeanDesc implements Serializable { // 检查注解 if (false == isTransient) { - isTransient = null != AnnotationUtil.getAnnotation(this.getter, Transient.class); + isTransient = AnnotationUtil.hasAnnotation(this.getter, Transient.class); } } return isTransient; } + /** + * 检查字段是否被忽略读,通过{@link Ignore} 注解完成,规则为: + *
+		 *     1. 在字段上有{@link Ignore} 注解
+		 *     2. 在getXXX方法上有{@link Ignore} 注解
+		 * 
+ * + * @return 是否忽略读 + * @since 5.4.2 + */ + public boolean isIgnoreGet() { + return AnnotationUtil.hasAnnotation(this.field, Ignore.class) + || AnnotationUtil.hasAnnotation(this.getter, Ignore.class); + } + + /** + * 检查字段是否被忽略写,通过{@link Ignore} 注解完成,规则为: + *
+		 *     1. 在字段上有{@link Ignore} 注解
+		 *     2. 在setXXX方法上有{@link Ignore} 注解
+		 * 
+ * + * @return 是否忽略写 + * @since 5.4.2 + */ + public boolean isIgnoreSet() { + return AnnotationUtil.hasAnnotation(this.field, Ignore.class) + || AnnotationUtil.hasAnnotation(this.setter, Ignore.class); + } + //------------------------------------------------------------------------------------ Private method start /** diff --git a/hutool-core/src/main/java/cn/hutool/core/bean/BeanDescCache.java b/hutool-core/src/main/java/cn/hutool/core/bean/BeanDescCache.java index c60999232..e7cf37cc4 100644 --- a/hutool-core/src/main/java/cn/hutool/core/bean/BeanDescCache.java +++ b/hutool-core/src/main/java/cn/hutool/core/bean/BeanDescCache.java @@ -1,6 +1,7 @@ package cn.hutool.core.bean; import cn.hutool.core.lang.SimpleCache; +import cn.hutool.core.lang.func.Func0; /** * Bean属性缓存
@@ -16,18 +17,11 @@ public enum BeanDescCache { /** * 获得属性名和{@link BeanDesc}Map映射 * @param beanClass Bean的类 + * @param supplier 对象不存在时创建对象的函数 * @return 属性名和{@link BeanDesc}映射 + * @since 5.4.2 */ - public BeanDesc getBeanDesc(Class beanClass){ - return bdCache.get(beanClass); - } - - /** - * 加入缓存 - * @param beanClass Bean的类 - * @param BeanDesc 属性名和{@link BeanDesc}映射 - */ - public void putBeanDesc(Class beanClass, BeanDesc BeanDesc){ - bdCache.put(beanClass, BeanDesc); + public BeanDesc getBeanDesc(Class beanClass, Func0 supplier){ + return bdCache.get(beanClass, supplier); } } diff --git a/hutool-core/src/main/java/cn/hutool/core/bean/BeanUtil.java b/hutool-core/src/main/java/cn/hutool/core/bean/BeanUtil.java index 4aa6f8047..f7e68e8bc 100644 --- a/hutool-core/src/main/java/cn/hutool/core/bean/BeanUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/bean/BeanUtil.java @@ -166,12 +166,7 @@ public class BeanUtil { * @since 3.1.2 */ public static BeanDesc getBeanDesc(Class clazz) { - BeanDesc beanDesc = BeanDescCache.INSTANCE.getBeanDesc(clazz); - if (null == beanDesc) { - beanDesc = new BeanDesc(clazz); - BeanDescCache.INSTANCE.putBeanDesc(clazz, beanDesc); - } - return beanDesc; + return BeanDescCache.INSTANCE.getBeanDesc(clazz, ()-> new BeanDesc(clazz)); } // --------------------------------------------------------------------------------------------------------- PropertyDescriptor diff --git a/hutool-core/src/main/java/cn/hutool/core/bean/copier/BeanCopier.java b/hutool-core/src/main/java/cn/hutool/core/bean/copier/BeanCopier.java index 35d88a75f..756c260f4 100644 --- a/hutool-core/src/main/java/cn/hutool/core/bean/copier/BeanCopier.java +++ b/hutool-core/src/main/java/cn/hutool/core/bean/copier/BeanCopier.java @@ -1,12 +1,11 @@ package cn.hutool.core.bean.copier; import cn.hutool.core.bean.BeanDesc.PropDesc; +import cn.hutool.core.bean.BeanException; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.copier.provider.BeanValueProvider; import cn.hutool.core.bean.copier.provider.MapValueProvider; import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.convert.Convert; -import cn.hutool.core.exceptions.UtilException; import cn.hutool.core.lang.copier.Copier; import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.ModifierUtil; @@ -23,7 +22,14 @@ import java.util.HashSet; import java.util.Map; /** - * Bean拷贝 + * Bean拷贝,提供: + * + *
+ *     1. Bean 转 Bean
+ *     2. Bean 转 Map
+ *     3. Map  转 Bean
+ *     4. Map  转 Map
+ * 
* * @author looly * @@ -158,36 +164,33 @@ public class BeanCopier implements Copier, Serializable { final CopyOptions copyOptions = this.copyOptions; String key; - Method getter; Object value; for (PropDesc prop : props) { - key = prop.getFieldName(); - // 过滤class属性 - // 得到property对应的getter方法 - getter = prop.getGetter(); - if (null != getter) { - // 只读取有getter方法的属性 - try { - value = getter.invoke(bean); - } catch (Exception e) { - if (copyOptions.ignoreError) { - continue;// 忽略反射失败 - } else { - throw new UtilException(e, "Get value of [{}] error!", prop.getFieldName()); - } - } - if (CollUtil.contains(ignoreSet, key)) { - // 目标属性值被忽略或值提供者无此key时跳过 - continue; - } - if (null == value && copyOptions.ignoreNullValue) { - continue;// 当允许跳过空时,跳过 - } - if (bean.equals(value)) { - continue;// 值不能为bean本身,防止循环引用 - } - targetMap.put(mappingKey(copyOptions.fieldMapping, key), value); + // 忽略注解的字段 + if(prop.isIgnoreGet()){ + continue; } + key = prop.getFieldName(); + if (CollUtil.contains(ignoreSet, key)) { + // 目标属性值被忽略或值提供者无此key时跳过 + continue; + } + try { + value = prop.getValue(bean); + } catch (Exception e) { + if (copyOptions.ignoreError) { + continue;// 忽略反射失败 + } else { + throw new BeanException(e, "Get value of [{}] error!", prop.getFieldName()); + } + } + if (null == value && copyOptions.ignoreNullValue) { + continue;// 当允许跳过空时,跳过 + } + if (bean.equals(value)) { + continue;// 值不能为bean本身,防止循环引用 + } + targetMap.put(mappingKey(copyOptions.fieldMapping, key), value); } } @@ -228,6 +231,17 @@ public class BeanCopier implements Copier, Serializable { // 目标属性值被忽略或值提供者无此key时跳过 continue; } + + // 在支持情况下,忽略transient修饰(包括修饰和注解) + if(copyOptions.isTransientSupport() && prop.isTransient()){ + continue; + } + + // @Ignore修饰的字段和setXXX方法忽略之 + if(prop.isIgnoreSet()){ + continue; + } + final String providerKey = mappingKey(fieldReverseMapping, fieldName); if (false == valueProvider.containsKey(providerKey)) { // 无对应值可提供 @@ -241,34 +255,18 @@ public class BeanCopier implements Copier, Serializable { } // 获取目标字段真实类型 - Type fieldType = (null == setterMethod) ? TypeUtil.getType(field) : TypeUtil.getFirstParamType(setterMethod); - fieldType = TypeUtil.getActualType(this.destType ,fieldType); + final Type fieldType = TypeUtil.getActualType(this.destType ,prop.getFieldType()); value = valueProvider.value(providerKey, fieldType); if (null == value && copyOptions.ignoreNullValue) { continue;// 当允许跳过空时,跳过 } if (bean == value) { - continue;// 值不能为bean本身,防止循环引用 + // 值不能为bean本身,防止循环引用 + continue; } - try { - // valueProvider在没有对值做转换且当类型不匹配的时候,执行默认转换 - propClass = prop.getFieldClass(); - if (false ==propClass.isInstance(value)) { - value = Convert.convertWithCheck(propClass, value, null, copyOptions.ignoreError); - if (null == value && copyOptions.ignoreNullValue) { - continue;// 当允许跳过空时,跳过 - } - } - - prop.setValue(bean, value); - } catch (Exception e) { - if (false ==copyOptions.ignoreError) { - throw new UtilException(e, "Inject [{}] error!", prop.getFieldName()); - } - // 忽略注入失败 - } + prop.setValueWithConvert(bean, value, copyOptions.ignoreNullValue, copyOptions.ignoreError); } } diff --git a/hutool-core/src/main/java/cn/hutool/core/bean/copier/CopyOptions.java b/hutool-core/src/main/java/cn/hutool/core/bean/copier/CopyOptions.java index 7ffebd75c..b588db654 100644 --- a/hutool-core/src/main/java/cn/hutool/core/bean/copier/CopyOptions.java +++ b/hutool-core/src/main/java/cn/hutool/core/bean/copier/CopyOptions.java @@ -1,38 +1,54 @@ package cn.hutool.core.bean.copier; +import cn.hutool.core.map.MapUtil; + import java.io.Serializable; import java.util.Map; -import cn.hutool.core.map.MapUtil; - /** * 属性拷贝选项
* 包括:
* 1、限制的类或接口,必须为目标对象的实现接口或父类,用于限制拷贝的属性,例如一个类我只想复制其父类的一些属性,就可以将editable设置为父类
* 2、是否忽略空值,当源对象的值为null时,true: 忽略而不注入此值,false: 注入null
* 3、忽略的属性列表,设置一个属性列表,不拷贝这些属性值
- * + * * @author Looly */ -public class CopyOptions implements Serializable{ +public class CopyOptions implements Serializable { private static final long serialVersionUID = 1L; - - /** 限制的类或接口,必须为目标对象的实现接口或父类,用于限制拷贝的属性,例如一个类我只想复制其父类的一些属性,就可以将editable设置为父类 */ + + /** + * 限制的类或接口,必须为目标对象的实现接口或父类,用于限制拷贝的属性,例如一个类我只想复制其父类的一些属性,就可以将editable设置为父类 + */ protected Class editable; - /** 是否忽略空值,当源对象的值为null时,true: 忽略而不注入此值,false: 注入null */ + /** + * 是否忽略空值,当源对象的值为null时,true: 忽略而不注入此值,false: 注入null + */ protected boolean ignoreNullValue; - /** 忽略的目标对象中属性列表,设置一个属性列表,不拷贝这些属性值 */ + /** + * 忽略的目标对象中属性列表,设置一个属性列表,不拷贝这些属性值 + */ protected String[] ignoreProperties; - /** 是否忽略字段注入错误 */ + /** + * 是否忽略字段注入错误 + */ protected boolean ignoreError; - /** 是否忽略字段大小写 */ + /** + * 是否忽略字段大小写 + */ protected boolean ignoreCase; - /** 拷贝属性的字段映射,用于不同的属性之前拷贝做对应表用 */ + /** + * 拷贝属性的字段映射,用于不同的属性之前拷贝做对应表用 + */ protected Map fieldMapping; + /** + * 是否支持transient关键字修饰和@Transient注解,如果支持,被修饰的字段或方法对应的字段将被忽略。 + */ + private boolean transientSupport = true; /** * 创建拷贝选项 - * + * * @return 拷贝选项 */ public static CopyOptions create() { @@ -41,9 +57,9 @@ public class CopyOptions implements Serializable{ /** * 创建拷贝选项 - * - * @param editable 限制的类或接口,必须为目标对象的实现接口或父类,用于限制拷贝的属性 - * @param ignoreNullValue 是否忽略空值,当源对象的值为null时,true: 忽略而不注入此值,false: 注入null + * + * @param editable 限制的类或接口,必须为目标对象的实现接口或父类,用于限制拷贝的属性 + * @param ignoreNullValue 是否忽略空值,当源对象的值为null时,true: 忽略而不注入此值,false: 注入null * @param ignoreProperties 忽略的属性列表,设置一个属性列表,不拷贝这些属性值 * @return 拷贝选项 */ @@ -59,9 +75,9 @@ public class CopyOptions implements Serializable{ /** * 构造拷贝选项 - * - * @param editable 限制的类或接口,必须为目标对象的实现接口或父类,用于限制拷贝的属性 - * @param ignoreNullValue 是否忽略空值,当源对象的值为null时,true: 忽略而不注入此值,false: 注入null + * + * @param editable 限制的类或接口,必须为目标对象的实现接口或父类,用于限制拷贝的属性 + * @param ignoreNullValue 是否忽略空值,当源对象的值为null时,true: 忽略而不注入此值,false: 注入null * @param ignoreProperties 忽略的目标对象中属性列表,设置一个属性列表,不拷贝这些属性值 */ public CopyOptions(Class editable, boolean ignoreNullValue, String... ignoreProperties) { @@ -72,7 +88,7 @@ public class CopyOptions implements Serializable{ /** * 设置限制的类或接口,必须为目标对象的实现接口或父类,用于限制拷贝的属性 - * + * * @param editable 限制的类或接口 * @return CopyOptions */ @@ -83,7 +99,7 @@ public class CopyOptions implements Serializable{ /** * 设置是否忽略空值,当源对象的值为null时,true: 忽略而不注入此值,false: 注入null - * + * * @param ignoreNullVall 是否忽略空值,当源对象的值为null时,true: 忽略而不注入此值,false: 注入null * @return CopyOptions */ @@ -91,10 +107,10 @@ public class CopyOptions implements Serializable{ this.ignoreNullValue = ignoreNullVall; return this; } - + /** * 设置忽略空值,当源对象的值为null时,忽略而不注入此值 - * + * * @return CopyOptions * @since 4.5.7 */ @@ -104,7 +120,7 @@ public class CopyOptions implements Serializable{ /** * 设置忽略的目标对象中属性列表,设置一个属性列表,不拷贝这些属性值 - * + * * @param ignoreProperties 忽略的目标对象中属性列表,设置一个属性列表,不拷贝这些属性值 * @return CopyOptions */ @@ -115,7 +131,7 @@ public class CopyOptions implements Serializable{ /** * 设置是否忽略字段的注入错误 - * + * * @param ignoreError 是否忽略注入错误 * @return CopyOptions */ @@ -123,20 +139,20 @@ public class CopyOptions implements Serializable{ this.ignoreError = ignoreError; return this; } - + /** * 设置忽略字段的注入错误 - * + * * @return CopyOptions * @since 4.5.7 */ public CopyOptions ignoreError() { return setIgnoreError(true); } - + /** * 设置是否忽略字段的大小写 - * + * * @param ignoreCase 是否忽略大小写 * @return CopyOptions */ @@ -144,10 +160,10 @@ public class CopyOptions implements Serializable{ this.ignoreCase = ignoreCase; return this; } - + /** * 设置忽略字段的大小写 - * + * * @return CopyOptions * @since 4.5.7 */ @@ -157,7 +173,7 @@ public class CopyOptions implements Serializable{ /** * 设置拷贝属性的字段映射,用于不同的属性之前拷贝做对应表用 - * + * * @param fieldMapping 拷贝属性的字段映射,用于不同的属性之前拷贝做对应表用 * @return CopyOptions */ @@ -165,13 +181,36 @@ public class CopyOptions implements Serializable{ this.fieldMapping = fieldMapping; return this; } - + + /** + * 是否支持transient关键字修饰和@Transient注解,如果支持,被修饰的字段或方法对应的字段将被忽略。 + * + * @return 是否支持 + * @since 5.4.2 + */ + public boolean isTransientSupport() { + return this.transientSupport; + } + + /** + * 设置是否支持transient关键字修饰和@Transient注解,如果支持,被修饰的字段或方法对应的字段将被忽略。 + * + * @param transientSupport 是否支持 + * @return this + * @since 5.4.2 + */ + public CopyOptions setTransientSupport(boolean transientSupport) { + this.transientSupport = transientSupport; + return this; + } + /** * 获取反转之后的映射 + * * @return 反转映射 * @since 4.1.10 */ protected Map getReversedMapping() { - return (null != this.fieldMapping) ? MapUtil.reverse(this.fieldMapping) : null; + return (null != this.fieldMapping) ? MapUtil.reverse(this.fieldMapping) : null; } } diff --git a/hutool-core/src/main/java/cn/hutool/core/bean/copier/provider/BeanValueProvider.java b/hutool-core/src/main/java/cn/hutool/core/bean/copier/provider/BeanValueProvider.java index 38bab46ed..d92c82641 100644 --- a/hutool-core/src/main/java/cn/hutool/core/bean/copier/provider/BeanValueProvider.java +++ b/hutool-core/src/main/java/cn/hutool/core/bean/copier/provider/BeanValueProvider.java @@ -3,11 +3,8 @@ package cn.hutool.core.bean.copier.provider; import cn.hutool.core.bean.BeanDesc.PropDesc; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.copier.ValueProvider; -import cn.hutool.core.convert.Convert; -import cn.hutool.core.exceptions.UtilException; import cn.hutool.core.util.StrUtil; -import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.Map; @@ -46,29 +43,32 @@ public class BeanValueProvider implements ValueProvider { Object result = null; if (null != sourcePd) { - final Method getter = sourcePd.getGetter(); - if (null != getter) { - try { - result = getter.invoke(source); - } catch (Exception e) { - if (false == ignoreError) { - throw new UtilException(e, "Inject [{}] error!", key); - } - } - - // 尝试将结果转换为目标类型,如果转换失败,返回原类型。 - final Object convertValue = Convert.convertWithCheck(valueType,result, null, ignoreError); - if(null != convertValue){ - result = convertValue; - } - } + result = sourcePd.getValueWithConvert(this.source, valueType, this.ignoreError); } return result; } @Override public boolean containsKey(String key) { - return sourcePdMap.containsKey(key) || sourcePdMap.containsKey(StrUtil.upperFirstAndAddPre(key, "is")); + PropDesc sourcePd = getPropDesc(key); + + // 字段描述不存在或忽略读的情况下,表示不存在 + return null != sourcePd && false == sourcePd.isIgnoreGet(); } + /** + * 获得属性描述 + * + * @param key 字段名 + * @return 属性描述 + */ + private PropDesc getPropDesc(String key){ + PropDesc sourcePd = sourcePdMap.get(key); + if(null == sourcePd) { + //boolean类型字段字段名支持两种方式 + sourcePd = sourcePdMap.get(StrUtil.upperFirstAndAddPre(key, "is")); + } + + return sourcePd; + } } diff --git a/hutool-core/src/main/java/cn/hutool/core/bean/copier/provider/MapValueProvider.java b/hutool-core/src/main/java/cn/hutool/core/bean/copier/provider/MapValueProvider.java index 2007776b6..3ec4851df 100644 --- a/hutool-core/src/main/java/cn/hutool/core/bean/copier/provider/MapValueProvider.java +++ b/hutool-core/src/main/java/cn/hutool/core/bean/copier/provider/MapValueProvider.java @@ -9,10 +9,10 @@ import java.lang.reflect.Type; import java.util.Map; /** - * Map值提供者 - * - * @author looly + * Map值提供者,支持驼峰和下划线的key兼容。
+ * 假设目标属性为firstName,则Map中为firstName或first_name都可以对应到值。 * + * @author looly */ public class MapValueProvider implements ValueProvider { @@ -22,7 +22,7 @@ public class MapValueProvider implements ValueProvider { /** * 构造 * - * @param map Map + * @param map Map * @param ignoreCase 是否忽略key的大小写 */ public MapValueProvider(Map map, boolean ignoreCase) { @@ -31,17 +31,17 @@ public class MapValueProvider implements ValueProvider { /** * 构造 - * - * @param map Map - * @param ignoreCase 是否忽略key的大小写 + * + * @param map Map + * @param ignoreCase 是否忽略key的大小写 * @param ignoreError 是否忽略错误 * @since 5.3.2 */ public MapValueProvider(Map map, boolean ignoreCase, boolean ignoreError) { - if(false == ignoreCase || map instanceof CaseInsensitiveMap) { + if (false == ignoreCase || map instanceof CaseInsensitiveMap) { //不忽略大小写或者提供的Map本身为CaseInsensitiveMap则无需转换 this.map = map; - }else { + } else { //转换为大小写不敏感的Map this.map = new CaseInsensitiveMap<>(map); } @@ -51,7 +51,7 @@ public class MapValueProvider implements ValueProvider { @Override public Object value(String key, Type valueType) { Object value = map.get(key); - if(null == value) { + if (null == value) { //检查下划线模式 value = map.get(StrUtil.toUnderlineCase(key)); } @@ -61,9 +61,9 @@ public class MapValueProvider implements ValueProvider { @Override public boolean containsKey(String key) { - if(map.containsKey(key)) { + if (map.containsKey(key)) { return true; - }else { + } else { //检查下划线模式 return map.containsKey(StrUtil.toUnderlineCase(key)); } diff --git a/hutool-json/src/main/java/cn/hutool/json/JSONConfig.java b/hutool-json/src/main/java/cn/hutool/json/JSONConfig.java index 7c1d49a0c..fd558ae82 100644 --- a/hutool-json/src/main/java/cn/hutool/json/JSONConfig.java +++ b/hutool-json/src/main/java/cn/hutool/json/JSONConfig.java @@ -32,9 +32,9 @@ public class JSONConfig implements Serializable { */ private boolean ignoreNullValue = true; /** - * 是否忽略transient关键字修饰的字段 + * 是否支持transient关键字修饰和@Transient注解,如果支持,被修饰的字段或方法对应的字段将被忽略。 */ - private boolean ignoreTransient = true; + private boolean transientSupport = true; /** * 创建默认的配置项 @@ -150,9 +150,11 @@ public class JSONConfig implements Serializable { * * @return 是否忽略transient关键字修饰的字段 * @since 5.3.11 + * @deprecated 此方法名称有二义性,请使用{@link #isTransientSupport()} */ + @Deprecated public boolean isIgnoreTransient() { - return this.ignoreTransient; + return isTransientSupport(); } /** @@ -161,9 +163,32 @@ public class JSONConfig implements Serializable { * @param ignoreTransient 是否忽略transient关键字修饰的字段 * @return this * @since 5.3.11 + * @deprecated 此方法名称有二义性,请使用{@link #setTransientSupport(boolean)} */ + @Deprecated public JSONConfig setIgnoreTransient(boolean ignoreTransient) { - this.ignoreTransient = ignoreTransient; + return setTransientSupport(ignoreTransient); + } + + /** + * 是否支持transient关键字修饰和@Transient注解,如果支持,被修饰的字段或方法对应的字段将被忽略。 + * + * @return 是否支持 + * @since 5.4.2 + */ + public boolean isTransientSupport() { + return this.transientSupport; + } + + /** + * 设置是否支持transient关键字修饰和@Transient注解,如果支持,被修饰的字段或方法对应的字段将被忽略。 + * + * @param transientSupport 是否支持 + * @return this + * @since 5.4.2 + */ + public JSONConfig setTransientSupport(boolean transientSupport) { + this.transientSupport = transientSupport; return this; } } diff --git a/hutool-json/src/main/java/cn/hutool/json/JSONObject.java b/hutool-json/src/main/java/cn/hutool/json/JSONObject.java index 580a7c417..011cbef69 100644 --- a/hutool-json/src/main/java/cn/hutool/json/JSONObject.java +++ b/hutool-json/src/main/java/cn/hutool/json/JSONObject.java @@ -625,7 +625,7 @@ public class JSONObject implements JSON, JSONGetter, Map Method getter; Object value; for (PropDesc prop : props) { - if(this.config.isIgnoreTransient() && prop.isTransient()){ + if(this.config.isTransientSupport() && prop.isTransient()){ // 忽略Transient字段和方法 continue; }