From f6f97668cf18995bb599cdd83d1de42ed17360b1 Mon Sep 17 00:00:00 2001 From: Looly Date: Fri, 31 Jul 2020 09:43:05 +0800 Subject: [PATCH] add config --- CHANGELOG.md | 1 + .../java/cn/hutool/core/bean/BeanDesc.java | 141 +++++++++++------- .../main/java/cn/hutool/json/JSONConfig.java | 73 ++++++--- .../main/java/cn/hutool/json/JSONObject.java | 5 + .../java/cn/hutool/json/TransientTest.java | 25 ++++ 5 files changed, 174 insertions(+), 71 deletions(-) create mode 100644 hutool-json/src/test/java/cn/hutool/json/TransientTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index dd87252ad..833dc595f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ * 【captcha】 AbstractCaptcha增加getImageBase64Data方法(pr#985@Github) * 【core 】 增加PhoneUtil(pr#990@Github) * 【core 】 改进Img,目标图片类型未定义使用源图片类型(issue#I1PB0B@Gitee) +* 【json 】 JSONConfig增加Transient选项(issue#I1PLHN@Gitee) ### Bug修复 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 fb41e24ff..c843bec87 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,5 +1,6 @@ package cn.hutool.core.bean; +import cn.hutool.core.annotation.AnnotationUtil; import cn.hutool.core.lang.Assert; import cn.hutool.core.map.CaseInsensitiveMap; import cn.hutool.core.util.BooleanUtil; @@ -9,6 +10,7 @@ import cn.hutool.core.util.ReflectUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.TypeUtil; +import java.beans.Transient; import java.io.Serializable; import java.lang.reflect.Field; import java.lang.reflect.Method; @@ -20,28 +22,32 @@ import java.util.Map; /** * Bean信息描述做为BeanInfo替代方案,此对象持有JavaBean中的setters和getters等相关信息描述
* 查找Getter和Setter方法时会: - * + * *
  * 1. 忽略字段和方法名的大小写
  * 2. Getter查找getXXX、isXXX、getIsXXX
  * 3. Setter查找setXXX、setIsXXX
  * 4. Setter忽略参数值与字段值不匹配的情况,因此有多个参数类型的重载时,会调用首次匹配的
  * 
- * + * * @author looly * @since 3.1.2 */ -public class BeanDesc implements Serializable{ +public class BeanDesc implements Serializable { private static final long serialVersionUID = 1L; - /** Bean类 */ + /** + * Bean类 + */ private final Class beanClass; - /** 属性Map */ + /** + * 属性Map + */ private final Map propMap = new LinkedHashMap<>(); /** * 构造 - * + * * @param beanClass Bean类 */ public BeanDesc(Class beanClass) { @@ -52,7 +58,7 @@ public class BeanDesc implements Serializable{ /** * 获取Bean的全类名 - * + * * @return Bean的类名 */ public String getName() { @@ -61,7 +67,7 @@ public class BeanDesc implements Serializable{ /** * 获取Bean的简单类名 - * + * * @return Bean的类名 */ public String getSimpleName() { @@ -70,7 +76,7 @@ public class BeanDesc implements Serializable{ /** * 获取字段名-字段属性Map - * + * * @param ignoreCase 是否忽略大小写,true为忽略,false不忽略 * @return 字段名-字段属性Map */ @@ -80,7 +86,7 @@ public class BeanDesc implements Serializable{ /** * 获取字段属性列表 - * + * * @return {@link PropDesc} 列表 */ public Collection getProps() { @@ -89,7 +95,7 @@ public class BeanDesc implements Serializable{ /** * 获取属性,如果不存在返回null - * + * * @param fieldName 字段名 * @return {@link PropDesc} */ @@ -99,7 +105,7 @@ public class BeanDesc implements Serializable{ /** * 获得字段名对应的字段对象,如果不存在返回null - * + * * @param fieldName 字段名 * @return 字段值 */ @@ -110,7 +116,7 @@ public class BeanDesc implements Serializable{ /** * 获取Getter方法,如果不存在返回null - * + * * @param fieldName 字段名 * @return Getter方法 */ @@ -121,7 +127,7 @@ public class BeanDesc implements Serializable{ /** * 获取Setter方法,如果不存在返回null - * + * * @param fieldName 字段名 * @return Setter方法 */ @@ -129,17 +135,18 @@ public class BeanDesc implements Serializable{ final PropDesc desc = this.propMap.get(fieldName); return null == desc ? null : desc.getSetter(); } - + // ------------------------------------------------------------------------------------------------------ Private method start + /** * 初始化
* 只有与属性关联的相关Getter和Setter方法才会被读取,无关的getXXX和setXXX都被忽略 - * + * * @return this */ private BeanDesc init() { for (Field field : ReflectUtil.getFields(this.beanClass)) { - if(false == ModifierUtil.isStatic(field)) { + if (false == ModifierUtil.isStatic(field)) { //只针对非static属性 this.propMap.put(ReflectUtil.getFieldName(field), createProp(field)); } @@ -150,14 +157,14 @@ public class BeanDesc implements Serializable{ /** * 根据字段创建属性描述
* 查找Getter和Setter方法时会: - * + * *
 	 * 1. 忽略字段和方法名的大小写
 	 * 2. Getter查找getXXX、isXXX、getIsXXX
 	 * 3. Setter查找setXXX、setIsXXX
 	 * 4. Setter忽略参数值与字段值不匹配的情况,因此有多个参数类型的重载时,会调用首次匹配的
 	 * 
- * + * * @param field 字段 * @return {@link PropDesc} * @since 4.0.2 @@ -201,7 +208,7 @@ public class BeanDesc implements Serializable{ /** * 方法是否为Getter方法
* 匹配规则如下(忽略大小写): - * + * *
 	 * 字段名    -》 方法名
 	 * isName  -》 isName
@@ -210,9 +217,9 @@ public class BeanDesc implements Serializable{
 	 * name     -》 isName
 	 * name     -》 getName
 	 * 
- * - * @param methodName 方法名 - * @param fieldName 字段名 + * + * @param methodName 方法名 + * @param fieldName 字段名 * @param isBooeanField 是否为Boolean类型字段 * @return 是否匹配 */ @@ -225,7 +232,7 @@ public class BeanDesc implements Serializable{ // 非标准Getter方法 return false; } - if("getclass".equals(methodName)) { + if ("getclass".equals(methodName)) { //跳过getClass方法 return false; } @@ -253,16 +260,16 @@ public class BeanDesc implements Serializable{ /** * 方法是否为Setter方法
* 匹配规则如下(忽略大小写): - * + * *
 	 * 字段名    -》 方法名
 	 * isName  -》 setName
 	 * isName  -》 setIsName
 	 * name     -》 setName
 	 * 
- * - * @param methodName 方法名 - * @param fieldName 字段名 + * + * @param methodName 方法名 + * @param fieldName 字段名 * @param isBooeanField 是否为Boolean类型字段 * @return 是否匹配 */ @@ -293,24 +300,29 @@ public class BeanDesc implements Serializable{ /** * 属性描述 - * - * @author looly * + * @author looly */ public static class PropDesc { - /** 字段 */ + /** + * 字段 + */ private final Field field; - /** Getter方法 */ + /** + * Getter方法 + */ private final Method getter; - /** Setter方法 */ + /** + * Setter方法 + */ private final Method setter; /** * 构造
* Getter和Setter方法设置为默认可访问 - * - * @param field 字段 + * + * @param field 字段 * @param getter get方法 * @param setter set方法 */ @@ -322,7 +334,7 @@ public class BeanDesc implements Serializable{ /** * 获取字段名,如果存在Alias注解,读取注解的值作为名称 - * + * * @return 字段名 */ public String getFieldName() { @@ -341,7 +353,7 @@ public class BeanDesc implements Serializable{ /** * 获取字段 - * + * * @return 字段 */ public Field getField() { @@ -351,7 +363,7 @@ public class BeanDesc implements Serializable{ /** * 获得字段类型
* 先获取字段的类型,如果字段不存在,则获取Getter方法的返回类型,否则获取Setter的第一个参数类型 - * + * * @return 字段类型 */ public Type getFieldType() { @@ -364,7 +376,7 @@ public class BeanDesc implements Serializable{ /** * 获得字段类型
* 先获取字段的类型,如果字段不存在,则获取Getter方法的返回类型,否则获取Setter的第一个参数类型 - * + * * @return 字段类型 */ public Class getFieldClass() { @@ -376,7 +388,7 @@ public class BeanDesc implements Serializable{ /** * 获取Getter方法,可能为{@code null} - * + * * @return Getter方法 */ public Method getGetter() { @@ -385,52 +397,75 @@ public class BeanDesc implements Serializable{ /** * 获取Setter方法,可能为{@code null} - * + * * @return {@link Method}Setter 方法对象 */ public Method getSetter() { return this.setter; } - + /** * 获取字段值
* 首先调用字段对应的Getter方法获取值,如果Getter方法不存在,则判断字段如果为public,则直接获取字段值 - * + * * @param bean Bean对象 * @return 字段值 * @since 4.0.5 */ public Object getValue(Object bean) { - if(null != this.getter) { + if (null != this.getter) { return ReflectUtil.invoke(bean, this.getter); - } else if(ModifierUtil.isPublic(this.field)) { + } else if (ModifierUtil.isPublic(this.field)) { return ReflectUtil.getFieldValue(bean, this.field); } return null; } - + /** * 设置Bean的字段值
* 首先调用字段对应的Setter方法,如果Setter方法不存在,则判断字段如果为public,则直接赋值字段值 - * - * @param bean Bean对象 + * + * @param bean Bean对象 * @param value 值 * @return this * @since 4.0.5 */ public PropDesc setValue(Object bean, Object value) { - if(null != this.setter) { + if (null != this.setter) { ReflectUtil.invoke(bean, this.setter, value); - } else if(ModifierUtil.isPublic(this.field)) { + } else if (ModifierUtil.isPublic(this.field)) { ReflectUtil.setFieldValue(bean, this.field, value); } return this; } - + + /** + * 字段和Getter方法是否为Transient关键字修饰的 + * + * @return 是否为Transient关键字修饰的 + * @since 5.3.11 + */ + public boolean isTransient() { + boolean isTransient = ModifierUtil.hasModifier(this.field, ModifierUtil.ModifierType.TRANSIENT); + + // 检查Getter方法 + if(false == isTransient && null != this.getter){ + isTransient = ModifierUtil.hasModifier(this.getter, ModifierUtil.ModifierType.TRANSIENT); + + // 检查注解 + if(false == isTransient){ + isTransient = null != AnnotationUtil.getAnnotation(this.getter, Transient.class); + } + } + + return isTransient; + } + //------------------------------------------------------------------------------------ Private method start + /** * 通过Getter和Setter方法中找到属性类型 - * + * * @param getter Getter方法 * @param setter Setter方法 * @return {@link Type} @@ -448,7 +483,7 @@ public class BeanDesc implements Serializable{ /** * 通过Getter和Setter方法中找到属性类型 - * + * * @param getter Getter方法 * @param setter Setter方法 * @return {@link Type} 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 9b6ba3a31..7c1d49a0c 100644 --- a/hutool-json/src/main/java/cn/hutool/json/JSONConfig.java +++ b/hutool-json/src/main/java/cn/hutool/json/JSONConfig.java @@ -4,26 +4,41 @@ import java.io.Serializable; /** * JSON配置项 - * + * * @author looly * @since 4.1.19 */ public class JSONConfig implements Serializable { private static final long serialVersionUID = 119730355204738278L; - /** 是否有序,顺序按照加入顺序排序 */ + /** + * 是否有序,顺序按照加入顺序排序 + */ private boolean order; - /** 是否忽略转换过程中的异常 */ + /** + * 是否忽略转换过程中的异常 + */ private boolean ignoreError; - /** 是否忽略键的大小写 */ + /** + * 是否忽略键的大小写 + */ private boolean ignoreCase; - /** 日期格式,null表示默认的时间戳 */ + /** + * 日期格式,null表示默认的时间戳 + */ private String dateFormat; - /** 是否忽略null值 */ + /** + * 是否忽略null值 + */ private boolean ignoreNullValue = true; - + /** + * 是否忽略transient关键字修饰的字段 + */ + private boolean ignoreTransient = true; + /** * 创建默认的配置项 + * * @return JSONConfig */ public static JSONConfig create() { @@ -32,7 +47,7 @@ public class JSONConfig implements Serializable { /** * 是否有序,顺序按照加入顺序排序 - * + * * @return 是否有序 */ public boolean isOrder() { @@ -41,7 +56,7 @@ public class JSONConfig implements Serializable { /** * 设置是否有序,顺序按照加入顺序排序 - * + * * @param order 是否有序 * @return this */ @@ -52,7 +67,7 @@ public class JSONConfig implements Serializable { /** * 是否忽略转换过程中的异常 - * + * * @return 是否忽略转换过程中的异常 */ public boolean isIgnoreError() { @@ -61,7 +76,7 @@ public class JSONConfig implements Serializable { /** * 设置是否忽略转换过程中的异常 - * + * * @param ignoreError 是否忽略转换过程中的异常 * @return this */ @@ -72,7 +87,7 @@ public class JSONConfig implements Serializable { /** * 是否忽略键的大小写 - * + * * @return 是否忽略键的大小写 */ public boolean isIgnoreCase() { @@ -81,7 +96,7 @@ public class JSONConfig implements Serializable { /** * 设置是否忽略键的大小写 - * + * * @param ignoreCase 是否忽略键的大小写 * @return this */ @@ -92,7 +107,7 @@ public class JSONConfig implements Serializable { /** * 日期格式,null表示默认的时间戳 - * + * * @return 日期格式,null表示默认的时间戳 */ public String getDateFormat() { @@ -101,7 +116,7 @@ public class JSONConfig implements Serializable { /** * 设置日期格式,null表示默认的时间戳 - * + * * @param dateFormat 日期格式,null表示默认的时间戳 * @return this */ @@ -109,10 +124,10 @@ public class JSONConfig implements Serializable { this.dateFormat = dateFormat; return this; } - + /** * 是否忽略null值 - * + * * @return 是否忽略null值 */ public boolean isIgnoreNullValue() { @@ -121,7 +136,7 @@ public class JSONConfig implements Serializable { /** * 设置是否忽略null值 - * + * * @param ignoreNullValue 是否忽略null值 * @return this */ @@ -129,4 +144,26 @@ public class JSONConfig implements Serializable { this.ignoreNullValue = ignoreNullValue; return this; } + + /** + * 是否忽略transient关键字修饰的字段 + * + * @return 是否忽略transient关键字修饰的字段 + * @since 5.3.11 + */ + public boolean isIgnoreTransient() { + return this.ignoreTransient; + } + + /** + * 设置是否忽略transient关键字修饰的字段 + * + * @param ignoreTransient 是否忽略transient关键字修饰的字段 + * @return this + * @since 5.3.11 + */ + public JSONConfig setIgnoreTransient(boolean ignoreTransient) { + this.ignoreTransient = ignoreTransient; + 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 765656d2f..580a7c417 100644 --- a/hutool-json/src/main/java/cn/hutool/json/JSONObject.java +++ b/hutool-json/src/main/java/cn/hutool/json/JSONObject.java @@ -625,6 +625,11 @@ public class JSONObject implements JSON, JSONGetter, Map Method getter; Object value; for (PropDesc prop : props) { + if(this.config.isIgnoreTransient() && prop.isTransient()){ + // 忽略Transient字段和方法 + continue; + } + // 得到property对应的getter方法 getter = prop.getGetter(); if (null == getter) { diff --git a/hutool-json/src/test/java/cn/hutool/json/TransientTest.java b/hutool-json/src/test/java/cn/hutool/json/TransientTest.java new file mode 100644 index 000000000..7f6509aa2 --- /dev/null +++ b/hutool-json/src/test/java/cn/hutool/json/TransientTest.java @@ -0,0 +1,25 @@ +package cn.hutool.json; + +import lombok.Data; +import org.junit.Assert; +import org.junit.Test; + +public class TransientTest { + + @Data + static class Bill{ + private transient String id; + private String bizNo; + } + + @Test + public void beanWithTransientTest(){ + Bill detailBill = new Bill(); + detailBill.setId("3243"); + detailBill.setBizNo("bizNo"); + + final JSONObject jsonObject = new JSONObject(detailBill, + JSONConfig.create().setIgnoreTransient(true)); + Assert.assertEquals("{\"bizNo\":\"bizNo\"}", jsonObject.toString()); + } +}