mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-04-19 03:01:48 +08:00
add methods
This commit is contained in:
parent
2212ee7705
commit
46764f02b8
@ -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过来的玩笑导致的问题
|
||||
|
@ -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<? extends Annotation> annotationType) {
|
||||
return null != getAnnotation(annotationEle, annotationType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定注解默认值<br>
|
||||
* 如果无指定的属性方法返回null
|
||||
|
@ -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 {
|
||||
|
||||
}
|
@ -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 {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取字段值<br>
|
||||
* 获取属性值<br>
|
||||
* 首先调用字段对应的Getter方法获取值,如果Getter方法不存在,则判断字段如果为public,则直接获取字段值
|
||||
*
|
||||
* @param bean Bean对象
|
||||
@ -455,12 +457,42 @@ public class BeanDesc implements Serializable {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取属性值,自动转换属性值类型<br>
|
||||
* 首先调用字段对应的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的字段值<br>
|
||||
* 首先调用字段对应的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} 注解完成,规则为:
|
||||
* <pre>
|
||||
* 1. 在字段上有{@link Ignore} 注解
|
||||
* 2. 在getXXX方法上有{@link Ignore} 注解
|
||||
* </pre>
|
||||
*
|
||||
* @return 是否忽略读
|
||||
* @since 5.4.2
|
||||
*/
|
||||
public boolean isIgnoreGet() {
|
||||
return AnnotationUtil.hasAnnotation(this.field, Ignore.class)
|
||||
|| AnnotationUtil.hasAnnotation(this.getter, Ignore.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查字段是否被忽略写,通过{@link Ignore} 注解完成,规则为:
|
||||
* <pre>
|
||||
* 1. 在字段上有{@link Ignore} 注解
|
||||
* 2. 在setXXX方法上有{@link Ignore} 注解
|
||||
* </pre>
|
||||
*
|
||||
* @return 是否忽略写
|
||||
* @since 5.4.2
|
||||
*/
|
||||
public boolean isIgnoreSet() {
|
||||
return AnnotationUtil.hasAnnotation(this.field, Ignore.class)
|
||||
|| AnnotationUtil.hasAnnotation(this.setter, Ignore.class);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------ Private method start
|
||||
|
||||
/**
|
||||
|
@ -1,6 +1,7 @@
|
||||
package cn.hutool.core.bean;
|
||||
|
||||
import cn.hutool.core.lang.SimpleCache;
|
||||
import cn.hutool.core.lang.func.Func0;
|
||||
|
||||
/**
|
||||
* Bean属性缓存<br>
|
||||
@ -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<BeanDesc> supplier){
|
||||
return bdCache.get(beanClass, supplier);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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拷贝,提供:
|
||||
*
|
||||
* <pre>
|
||||
* 1. Bean 转 Bean
|
||||
* 2. Bean 转 Map
|
||||
* 3. Map 转 Bean
|
||||
* 4. Map 转 Map
|
||||
* </pre>
|
||||
*
|
||||
* @author looly
|
||||
*
|
||||
@ -158,36 +164,33 @@ public class BeanCopier<T> implements Copier<T>, 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<T> implements Copier<T>, 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<T> implements Copier<T>, 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
/**
|
||||
* 属性拷贝选项<br>
|
||||
* 包括:<br>
|
||||
* 1、限制的类或接口,必须为目标对象的实现接口或父类,用于限制拷贝的属性,例如一个类我只想复制其父类的一些属性,就可以将editable设置为父类<br>
|
||||
* 2、是否忽略空值,当源对象的值为null时,true: 忽略而不注入此值,false: 注入null<br>
|
||||
* 3、忽略的属性列表,设置一个属性列表,不拷贝这些属性值<br>
|
||||
*
|
||||
*
|
||||
* @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<String, String> 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<String, String> getReversedMapping() {
|
||||
return (null != this.fieldMapping) ? MapUtil.reverse(this.fieldMapping) : null;
|
||||
return (null != this.fieldMapping) ? MapUtil.reverse(this.fieldMapping) : null;
|
||||
}
|
||||
}
|
||||
|
@ -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<String> {
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -9,10 +9,10 @@ import java.lang.reflect.Type;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Map值提供者
|
||||
*
|
||||
* @author looly
|
||||
* Map值提供者,支持驼峰和下划线的key兼容。<br>
|
||||
* 假设目标属性为firstName,则Map中为firstName或first_name都可以对应到值。
|
||||
*
|
||||
* @author looly
|
||||
*/
|
||||
public class MapValueProvider implements ValueProvider<String> {
|
||||
|
||||
@ -22,7 +22,7 @@ public class MapValueProvider implements ValueProvider<String> {
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param map Map
|
||||
* @param map Map
|
||||
* @param ignoreCase 是否忽略key的大小写
|
||||
*/
|
||||
public MapValueProvider(Map<?, ?> map, boolean ignoreCase) {
|
||||
@ -31,17 +31,17 @@ public class MapValueProvider implements ValueProvider<String> {
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @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<String> {
|
||||
@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<String> {
|
||||
|
||||
@Override
|
||||
public boolean containsKey(String key) {
|
||||
if(map.containsKey(key)) {
|
||||
if (map.containsKey(key)) {
|
||||
return true;
|
||||
}else {
|
||||
} else {
|
||||
//检查下划线模式
|
||||
return map.containsKey(StrUtil.toUnderlineCase(key));
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -625,7 +625,7 @@ public class JSONObject implements JSON, JSONGetter<String>, Map<String, Object>
|
||||
Method getter;
|
||||
Object value;
|
||||
for (PropDesc prop : props) {
|
||||
if(this.config.isIgnoreTransient() && prop.isTransient()){
|
||||
if(this.config.isTransientSupport() && prop.isTransient()){
|
||||
// 忽略Transient字段和方法
|
||||
continue;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user