From eec27cdc4079dd9e3951fae6fd8b5eacb06904d4 Mon Sep 17 00:00:00 2001 From: Looly Date: Tue, 10 Sep 2024 20:19:36 +0800 Subject: [PATCH] fix code --- .../dromara/hutool/core/bean/BeanDesc.java | 7 +- .../dromara/hutool/core/bean/PropDesc.java | 205 ++++++++++-------- .../hutool/core/bean/SimpleBeanDesc.java | 10 +- .../core/convert/RegisterConverter.java | 2 - .../hutool/core/convert/SpecialConverter.java | 4 + .../core/convert/impl/TimeZoneConverter.java | 15 +- .../core/convert/impl/ZoneIdConverter.java | 15 +- .../dromara/hutool/core/date/DateTime.java | 2 +- .../dromara/hutool/core/func/LambdaUtil.java | 9 +- .../org/dromara/hutool/core/lang/Opt.java | 13 ++ .../hutool/core/reflect/FieldInvoker.java | 45 +++- .../dromara/hutool/core/reflect/Invoker.java | 20 +- .../dromara/hutool/core/reflect/TypeUtil.java | 9 +- .../core/reflect/method/MethodInvoker.java | 74 +++++-- .../dromara/hutool/core/util/BooleanUtil.java | 7 +- .../hutool/core/bean/BeanDescTest.java | 16 +- .../hutool/db/handler/row/BeanRowHandler.java | 15 +- .../java/org/dromara/hutool/json/JSON.java | 5 +- .../dromara/hutool/json/JSONPrimitive.java | 132 +++++++++++ .../org/dromara/hutool/json/JSONUtil.java | 24 +- .../hutool/json/convert/JSONConverter.java | 54 +++-- .../hutool/json/engine/HutoolJSONEngine.java | 4 +- .../hutool/json/engine/gson/GsonEngine.java | 16 +- .../json/engine/gson/TimeZoneSerDesc.java | 46 ++++ .../json/engine/jackson/JacksonEngine.java | 6 +- .../serialize/JSONPrimitiveSerializer.java | 29 +++ .../dromara/hutool/json/IssueI6LBZATest.java | 19 +- .../dromara/hutool/json/IssueI7M2GZTest.java | 2 +- .../org/dromara/hutool/json/JSONUtilTest.java | 16 +- .../hutool/json/engine/JSONEngineTest.java | 19 ++ 30 files changed, 618 insertions(+), 222 deletions(-) create mode 100644 hutool-json/src/main/java/org/dromara/hutool/json/JSONPrimitive.java create mode 100644 hutool-json/src/main/java/org/dromara/hutool/json/engine/gson/TimeZoneSerDesc.java create mode 100644 hutool-json/src/main/java/org/dromara/hutool/json/serialize/JSONPrimitiveSerializer.java diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/bean/BeanDesc.java b/hutool-core/src/main/java/org/dromara/hutool/core/bean/BeanDesc.java index 6a7104dd9..5e88a6804 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/bean/BeanDesc.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/bean/BeanDesc.java @@ -16,8 +16,9 @@ package org.dromara.hutool.core.bean; +import org.dromara.hutool.core.reflect.Invoker; + import java.io.Serializable; -import java.lang.reflect.Method; import java.util.Collection; import java.util.Map; @@ -62,7 +63,7 @@ public interface BeanDesc extends Serializable { * @param fieldName 字段名 * @return Getter方法 */ - default Method getGetter(final String fieldName) { + default Invoker getGetter(final String fieldName) { final PropDesc desc = getProp(fieldName); return null == desc ? null : desc.getGetter(); } @@ -73,7 +74,7 @@ public interface BeanDesc extends Serializable { * @param fieldName 字段名 * @return Setter方法 */ - default Method getSetter(final String fieldName) { + default Invoker getSetter(final String fieldName) { final PropDesc desc = getProp(fieldName); return null == desc ? null : desc.getSetter(); } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/bean/PropDesc.java b/hutool-core/src/main/java/org/dromara/hutool/core/bean/PropDesc.java index 4a8a90de5..8cb40558c 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/bean/PropDesc.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/bean/PropDesc.java @@ -20,7 +20,7 @@ import org.dromara.hutool.core.annotation.AnnotationUtil; import org.dromara.hutool.core.annotation.PropIgnore; import org.dromara.hutool.core.convert.ConvertUtil; import org.dromara.hutool.core.reflect.*; -import org.dromara.hutool.core.reflect.method.MethodUtil; +import org.dromara.hutool.core.reflect.method.MethodInvoker; import java.beans.Transient; import java.lang.reflect.Field; @@ -37,7 +37,7 @@ public class PropDesc { /** * 字段 */ - private Field field; + private Invoker fieldInvoker; /** * 字段名 */ @@ -45,11 +45,11 @@ public class PropDesc { /** * Getter方法 */ - protected Method getter; + protected Invoker getter; /** * Setter方法 */ - protected Method setter; + protected Invoker setter; /** * 构造
@@ -61,21 +61,21 @@ public class PropDesc { */ public PropDesc(final Field field, final Method getter, final Method setter) { this(FieldUtil.getFieldName(field), getter, setter); - this.field = field; + this.fieldInvoker = null == field ? null : FieldInvoker.of(field); } /** * 构造
* Getter和Setter方法设置为默认可访问 * - * @param fieldName 字段名 - * @param getter get方法 - * @param setter set方法 + * @param fieldName 字段名 + * @param getterMethod get方法 + * @param setterMethod set方法 */ - public PropDesc(final String fieldName, final Method getter, final Method setter) { + public PropDesc(final String fieldName, final Method getterMethod, final Method setterMethod) { this.fieldName = fieldName; - this.getter = ReflectUtil.setAccessible(getter); - this.setter = ReflectUtil.setAccessible(setter); + this.getter = null == getterMethod ? null : MethodInvoker.of(getterMethod); + this.setter = null == setterMethod ? null : MethodInvoker.of(setterMethod); } /** @@ -94,7 +94,11 @@ public class PropDesc { * @since 5.1.6 */ public String getRawFieldName() { - return null == this.field ? null : this.field.getName(); + if (null == this.fieldInvoker) { + return this.fieldName; + } + + return this.fieldInvoker.getName(); } /** @@ -103,7 +107,10 @@ public class PropDesc { * @return 字段 */ public Field getField() { - return this.field; + if (null != this.fieldInvoker && this.fieldInvoker instanceof FieldInvoker) { + return ((FieldInvoker) this.fieldInvoker).getField(); + } + return null; } /** @@ -113,8 +120,8 @@ public class PropDesc { * @return 字段类型 */ public Type getFieldType() { - if (null != this.field) { - return TypeUtil.getType(this.field); + if (null != this.fieldInvoker) { + return this.fieldInvoker.getType(); } return findPropType(getter, setter); } @@ -126,27 +133,27 @@ public class PropDesc { * @return 字段类型 */ public Class getFieldClass() { - if (null != this.field) { - return TypeUtil.getClass(this.field); + if (null != this.fieldInvoker) { + return this.fieldInvoker.getTypeClass(); } return findPropClass(getter, setter); } /** - * 获取Getter方法,可能为{@code null} + * 获取Getter方法Invoker,可能为{@code null} * - * @return Getter方法 + * @return Getter方法Invoker */ - public Method getGetter() { + public Invoker getGetter() { return this.getter; } /** - * 获取Setter方法,可能为{@code null} + * 获取Setter方法Invoker,可能为{@code null} * - * @return {@link Method}Setter 方法对象 + * @return {@link Method}Setter 方法Invoker */ - public Method getSetter() { + public Invoker getSetter() { return this.setter; } @@ -158,18 +165,27 @@ public class PropDesc { * @since 5.4.2 */ public boolean isReadable(final boolean checkTransient) { - // 检查是否有getter方法或是否为public修饰 - if (null == this.getter && !ModifierUtil.isPublic(this.field)) { - return false; + Field field = null; + if (this.fieldInvoker instanceof FieldInvoker) { + field = ((FieldInvoker) this.fieldInvoker).getField(); + } + Method getterMethod = null; + if (this.getter instanceof MethodInvoker) { + getterMethod = ((MethodInvoker) this.getter).getMethod(); } // 检查transient关键字和@Transient注解 - if (checkTransient && isTransientForGet()) { + if (checkTransient && isTransientForGet(field, getterMethod)) { return false; } // 检查@PropIgnore注解 - return !isIgnoreGet(); + if (isIgnoreGet(field, getterMethod)) { + return false; + } + + // 检查是否有getter方法或是否为public修饰 + return null != getterMethod || ModifierUtil.isPublic(field); } /** @@ -183,11 +199,9 @@ public class PropDesc { */ public Object getValue(final Object bean) { if (null != this.getter) { - // issue#3671 JDK15+ 修改了lambda的策略,动态生成后在metaspace不会释放,导致资源占用高 - //return LambdaUtil.buildGetter(this.getter).apply(bean); - return MethodUtil.invoke(bean, this.getter); - } else if (ModifierUtil.isPublic(this.field)) { - return FieldUtil.getFieldValue(bean, this.field); + return this.getter.invoke(bean); + } else if (null != this.fieldInvoker) { + return fieldInvoker.invoke(bean); } return null; @@ -230,18 +244,27 @@ public class PropDesc { * @since 5.4.2 */ public boolean isWritable(final boolean checkTransient) { - // 检查是否有getter方法或是否为public修饰 - if (null == this.setter && !ModifierUtil.isPublic(this.field)) { - return false; + Field field = null; + if (this.fieldInvoker instanceof FieldInvoker) { + field = ((FieldInvoker) this.fieldInvoker).getField(); + } + Method setterMethod = null; + if (this.setter instanceof MethodInvoker) { + setterMethod = ((MethodInvoker) this.setter).getMethod(); } // 检查transient关键字和@Transient注解 - if (checkTransient && isTransientForSet()) { + if (checkTransient && isTransientForSet(field, setterMethod)) { return false; } // 检查@PropIgnore注解 - return !isIgnoreSet(); + if(isIgnoreSet(field, setterMethod)){ + return false; + } + + // 检查是否有setter方法或是否为public修饰 + return null != setterMethod || ModifierUtil.isPublic(field); } /** @@ -256,9 +279,9 @@ public class PropDesc { */ public PropDesc setValue(final Object bean, final Object value) { if (null != this.setter) { - MethodUtil.invoke(bean, this.setter, value); - } else if (ModifierUtil.isPublic(this.field)) { - FieldUtil.setFieldValue(bean, this.field, value); + this.setter.invoke(bean, value); + } else if (null != this.fieldInvoker) { + fieldInvoker.invoke(bean, value); } return this; } @@ -325,29 +348,29 @@ public class PropDesc { @Override public String toString() { return "PropDesc{" + - "field=" + field + + "field=" + fieldInvoker + ", fieldName=" + fieldName + ", getter=" + getter + ", setter=" + setter + '}'; } - //------------------------------------------------------------------------------------ Private method start + // region ----- private methods /** * 通过Getter和Setter方法中找到属性类型 * - * @param getter Getter方法 - * @param setter Setter方法 + * @param getterInvoker Getter方法Invoker + * @param setterInvoker Setter方法Invoker * @return {@link Type} */ - private Type findPropType(final Method getter, final Method setter) { + private Type findPropType(final Invoker getterInvoker, final Invoker setterInvoker) { Type type = null; - if (null != getter) { - type = TypeUtil.getReturnType(getter); + if (null != getterInvoker) { + type = getterInvoker.getType(); } - if (null == type && null != setter) { - type = TypeUtil.getParamType(setter, 0); + if (null == type && null != setterInvoker) { + type = setterInvoker.getType(); } return type; } @@ -355,36 +378,21 @@ public class PropDesc { /** * 通过Getter和Setter方法中找到属性类型 * - * @param getter Getter方法 - * @param setter Setter方法 + * @param getterInvoker Getter方法Invoker + * @param setterInvoker Setter方法Invoker * @return {@link Type} */ - private Class findPropClass(final Method getter, final Method setter) { + private Class findPropClass(final Invoker getterInvoker, final Invoker setterInvoker) { Class type = null; - if (null != getter) { - type = TypeUtil.getReturnClass(getter); + if (null != getterInvoker) { + type = getterInvoker.getTypeClass(); } - if (null == type && null != setter) { - type = TypeUtil.getFirstParamClass(setter); + if (null == type && null != setterInvoker) { + type = setterInvoker.getTypeClass(); } return type; } - /** - * 检查字段是否被忽略写,通过{@link PropIgnore} 注解完成,规则为: - *
-	 *     1. 在字段上有{@link PropIgnore} 注解
-	 *     2. 在setXXX方法上有{@link PropIgnore} 注解
-	 * 
- * - * @return 是否忽略写 - * @since 5.4.2 - */ - private boolean isIgnoreSet() { - return AnnotationUtil.hasAnnotation(this.field, PropIgnore.class) - || AnnotationUtil.hasAnnotation(this.setter, PropIgnore.class); - } - /** * 检查字段是否被忽略读,通过{@link PropIgnore} 注解完成,规则为: *
@@ -392,30 +400,48 @@ public class PropDesc {
 	 *     2. 在getXXX方法上有{@link PropIgnore} 注解
 	 * 
* + * @param field 字段,可为{@code null} + * @param getterMethod 读取方法,可为{@code null} * @return 是否忽略读 - * @since 5.4.2 */ - private boolean isIgnoreGet() { - return AnnotationUtil.hasAnnotation(this.field, PropIgnore.class) - || AnnotationUtil.hasAnnotation(this.getter, PropIgnore.class); + private static boolean isIgnoreGet(final Field field, final Method getterMethod) { + return AnnotationUtil.hasAnnotation(field, PropIgnore.class) + || AnnotationUtil.hasAnnotation(getterMethod, PropIgnore.class); + } + + /** + * 检查字段是否被忽略写,通过{@link PropIgnore} 注解完成,规则为: + *
+	 *     1. 在字段上有{@link PropIgnore} 注解
+	 *     2. 在setXXX方法上有{@link PropIgnore} 注解
+	 * 
+ * + * @param field 字段,可为{@code null} + * @param setterMethod 写方法,可为{@code null} + * @return 是否忽略写 + */ + private static boolean isIgnoreSet(final Field field, final Method setterMethod) { + return AnnotationUtil.hasAnnotation(field, PropIgnore.class) + || AnnotationUtil.hasAnnotation(setterMethod, PropIgnore.class); } /** * 字段和Getter方法是否为Transient关键字修饰的 * + * @param field 字段,可为{@code null} + * @param getterMethod 读取方法,可为{@code null} * @return 是否为Transient关键字修饰的 - * @since 5.3.11 */ - private boolean isTransientForGet() { - boolean isTransient = ModifierUtil.hasModifier(this.field, ModifierType.TRANSIENT); + private static boolean isTransientForGet(final Field field, final Method getterMethod) { + boolean isTransient = ModifierUtil.hasModifier(field, ModifierType.TRANSIENT); // 检查Getter方法 - if (!isTransient && null != this.getter) { - isTransient = ModifierUtil.hasModifier(this.getter, ModifierType.TRANSIENT); + if (!isTransient && null != getterMethod) { + isTransient = ModifierUtil.hasModifier(getterMethod, ModifierType.TRANSIENT); // 检查注解 if (!isTransient) { - isTransient = AnnotationUtil.hasAnnotation(this.getter, Transient.class); + isTransient = AnnotationUtil.hasAnnotation(getterMethod, Transient.class); } } @@ -425,23 +451,24 @@ public class PropDesc { /** * 字段和Getter方法是否为Transient关键字修饰的 * + * @param field 字段,可为{@code null} + * @param setterMethod 写方法,可为{@code null} * @return 是否为Transient关键字修饰的 - * @since 5.3.11 */ - private boolean isTransientForSet() { - boolean isTransient = ModifierUtil.hasModifier(this.field, ModifierType.TRANSIENT); + private static boolean isTransientForSet(final Field field, final Method setterMethod) { + boolean isTransient = ModifierUtil.hasModifier(field, ModifierType.TRANSIENT); // 检查Getter方法 - if (!isTransient && null != this.setter) { - isTransient = ModifierUtil.hasModifier(this.setter, ModifierType.TRANSIENT); + if (!isTransient && null != setterMethod) { + isTransient = ModifierUtil.hasModifier(setterMethod, ModifierType.TRANSIENT); // 检查注解 if (!isTransient) { - isTransient = AnnotationUtil.hasAnnotation(this.setter, Transient.class); + isTransient = AnnotationUtil.hasAnnotation(setterMethod, Transient.class); } } return isTransient; } - //------------------------------------------------------------------------------------ Private method end + // endregion } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/bean/SimpleBeanDesc.java b/hutool-core/src/main/java/org/dromara/hutool/core/bean/SimpleBeanDesc.java index 30fc5f7c4..9d8cba8b5 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/bean/SimpleBeanDesc.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/bean/SimpleBeanDesc.java @@ -17,6 +17,8 @@ package org.dromara.hutool.core.bean; import org.dromara.hutool.core.bean.path.AbstractBeanDesc; +import org.dromara.hutool.core.reflect.TypeUtil; +import org.dromara.hutool.core.reflect.method.MethodInvoker; import org.dromara.hutool.core.reflect.method.MethodNameUtil; import org.dromara.hutool.core.reflect.method.MethodUtil; import org.dromara.hutool.core.util.BooleanUtil; @@ -86,17 +88,17 @@ public class SimpleBeanDesc extends AbstractBeanDesc { } else{ if(isSetter){ if(null == propDesc.setter || - propDesc.setter.getParameterTypes()[0].isAssignableFrom(method.getParameterTypes()[0])){ + propDesc.setter.getTypeClass().isAssignableFrom(method.getParameterTypes()[0])){ // 如果存在多个重载的setter方法,选择参数类型最匹配的 - propDesc.setter = method; + propDesc.setter = MethodInvoker.of(method); } }else{ if(null == propDesc.getter || - (BooleanUtil.isBoolean(propDesc.getter.getReturnType()) && + (BooleanUtil.isBoolean(propDesc.getter.getTypeClass()) && BooleanUtil.isBoolean(method.getReturnType()) && methodName.startsWith(MethodNameUtil.IS_PREFIX))){ // 如果返回值为Boolean或boolean,isXXX优先于getXXX - propDesc.getter = method; + propDesc.getter = MethodInvoker.of(method); } } } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/RegisterConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/RegisterConverter.java index 96e2d2bcc..7a0b03aa3 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/RegisterConverter.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/RegisterConverter.java @@ -259,8 +259,6 @@ public class RegisterConverter implements Converter, Serializable { defaultConverterMap.put(AtomicLongArray.class, new AtomicLongArrayConverter()); // 其它类型 - defaultConverterMap.put(TimeZone.class, new TimeZoneConverter()); - defaultConverterMap.put(ZoneId.class, new ZoneIdConverter()); defaultConverterMap.put(Locale.class, new LocaleConverter()); defaultConverterMap.put(Charset.class, new CharsetConverter()); defaultConverterMap.put(Path.class, new PathConverter()); diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/SpecialConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/SpecialConverter.java index 575c8729e..91f242761 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/SpecialConverter.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/SpecialConverter.java @@ -94,6 +94,10 @@ public class SpecialConverter implements Converter, Serializable { // // 空值转空Bean converterSet.add(EmptyBeanConverter.INSTANCE); + // 日期相关 + converterSet.add(TimeZoneConverter.INSTANCE); + converterSet.add(ZoneIdConverter.INSTANCE); + this.converterSet = converterSet; } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/TimeZoneConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/TimeZoneConverter.java index d9332a274..fccfab9ab 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/TimeZoneConverter.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/TimeZoneConverter.java @@ -17,8 +17,10 @@ package org.dromara.hutool.core.convert.impl; import org.dromara.hutool.core.convert.AbstractConverter; +import org.dromara.hutool.core.convert.MatcherConverter; import org.dromara.hutool.core.date.ZoneUtil; +import java.lang.reflect.Type; import java.time.ZoneId; import java.util.TimeZone; @@ -27,9 +29,19 @@ import java.util.TimeZone; * @author Looly * */ -public class TimeZoneConverter extends AbstractConverter{ +public class TimeZoneConverter extends AbstractConverter implements MatcherConverter { private static final long serialVersionUID = 1L; + /** + * 单例 + */ + public static final TimeZoneConverter INSTANCE = new TimeZoneConverter(); + + @Override + public boolean match(final Type targetType, final Class rawType, final Object value) { + return TimeZone.class.isAssignableFrom(rawType); + } + @Override protected TimeZone convertInternal(final Class targetClass, final Object value) { if(value instanceof ZoneId){ @@ -37,5 +49,4 @@ public class TimeZoneConverter extends AbstractConverter{ } return TimeZone.getTimeZone(convertToStr(value)); } - } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/ZoneIdConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/ZoneIdConverter.java index 710ff346f..d9a0062a3 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/ZoneIdConverter.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/ZoneIdConverter.java @@ -17,8 +17,10 @@ package org.dromara.hutool.core.convert.impl; import org.dromara.hutool.core.convert.AbstractConverter; +import org.dromara.hutool.core.convert.MatcherConverter; import org.dromara.hutool.core.date.ZoneUtil; +import java.lang.reflect.Type; import java.time.ZoneId; import java.util.TimeZone; @@ -27,9 +29,19 @@ import java.util.TimeZone; * * @author Looly */ -public class ZoneIdConverter extends AbstractConverter { +public class ZoneIdConverter extends AbstractConverter implements MatcherConverter { private static final long serialVersionUID = 1L; + /** + * 单例 + */ + public static final ZoneIdConverter INSTANCE = new ZoneIdConverter(); + + @Override + public boolean match(final Type targetType, final Class rawType, final Object value) { + return ZoneId.class.isAssignableFrom(rawType); + } + @Override protected ZoneId convertInternal(final Class targetClass, final Object value) { if (value instanceof TimeZone) { @@ -37,5 +49,4 @@ public class ZoneIdConverter extends AbstractConverter { } return ZoneId.of(convertToStr(value)); } - } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/date/DateTime.java b/hutool-core/src/main/java/org/dromara/hutool/core/date/DateTime.java index c585c55b0..e3bf3b777 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/date/DateTime.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/date/DateTime.java @@ -80,7 +80,7 @@ public class DateTime extends Date { /** * 时区 */ - private TimeZone timeZone; + private transient TimeZone timeZone; /** * 第一周最少天数 diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/func/LambdaUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/func/LambdaUtil.java index c80cb2b5b..bd109497d 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/func/LambdaUtil.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/func/LambdaUtil.java @@ -24,6 +24,7 @@ import org.dromara.hutool.core.lang.Opt; import org.dromara.hutool.core.map.reference.WeakConcurrentMap; import org.dromara.hutool.core.reflect.ClassDescUtil; import org.dromara.hutool.core.reflect.ModifierUtil; +import org.dromara.hutool.core.reflect.method.MethodInvoker; import org.dromara.hutool.core.reflect.method.MethodUtil; import java.io.Serializable; @@ -171,9 +172,9 @@ public class LambdaUtil { * @param getter方法返回值类型 * @return Obj::getXxx */ - @SuppressWarnings("unchecked") public static Function buildGetter(final Class clazz, final String fieldName) { - return LambdaFactory.build(Function.class, BeanUtil.getBeanDesc(clazz).getGetter(fieldName)); + final MethodInvoker getter = (MethodInvoker) BeanUtil.getBeanDesc(clazz).getGetter(fieldName); + return buildGetter(getter.getMethod()); } /** @@ -205,9 +206,9 @@ public class LambdaUtil { * @param

setter方法返回的值类型 * @return Obj::setXxx */ - @SuppressWarnings("unchecked") public static BiConsumer buildSetter(final Class clazz, final String fieldName) { - return LambdaFactory.build(BiConsumer.class, BeanUtil.getBeanDesc(clazz).getSetter(fieldName)); + final MethodInvoker setter = (MethodInvoker) BeanUtil.getBeanDesc(clazz).getSetter(fieldName); + return buildSetter(setter.getMethod()); } /** diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/lang/Opt.java b/hutool-core/src/main/java/org/dromara/hutool/core/lang/Opt.java index 79e6dab7b..d20d11aa3 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/lang/Opt.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/lang/Opt.java @@ -163,6 +163,19 @@ public class Opt { return this.value; } + /** + * 返回包裹里的元素,取不到则抛出异常 + * + * @return 包裹里的元素 + * @throws NoSuchElementException 如果元素为空,则抛出此异常 + */ + public T getOrThrow() throws NoSuchElementException { + if (this.value == null) { + throw new NoSuchElementException("No value present"); + } + return this.value; + } + /** * 判断包裹里元素的值是否不存在,不存在为 {@code true},否则为{@code false} * diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/reflect/FieldInvoker.java b/hutool-core/src/main/java/org/dromara/hutool/core/reflect/FieldInvoker.java index 1dc054308..d92eed07b 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/reflect/FieldInvoker.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/reflect/FieldInvoker.java @@ -22,6 +22,7 @@ import org.dromara.hutool.core.exception.HutoolException; import org.dromara.hutool.core.lang.Assert; import java.lang.reflect.Field; +import java.lang.reflect.Type; /** * 字段调用器
@@ -30,7 +31,7 @@ import java.lang.reflect.Field; *

{@code
  *   FieldInvoker.of(Field).invoke(obj);
  * }
- * + *

* 赋值字段值: *

{@code
  *   FieldInvoker.of(Field).invoke(obj, value);
@@ -60,7 +61,32 @@ public class FieldInvoker implements Invoker {
 	 * @param field 字段
 	 */
 	public FieldInvoker(final Field field) {
-		this.field = Assert.notNull(field);;
+		this.field = Assert.notNull(field);
+		;
+	}
+
+	/**
+	 * 获取字段
+	 *
+	 * @return 字段
+	 */
+	public Field getField() {
+		return this.field;
+	}
+
+	@Override
+	public String getName() {
+		return this.field.getName();
+	}
+
+	@Override
+	public Type getType() {
+		return field.getGenericType();
+	}
+
+	@Override
+	public Class getTypeClass() {
+		return field.getType();
 	}
 
 	/**
@@ -77,10 +103,10 @@ public class FieldInvoker implements Invoker {
 	@SuppressWarnings("unchecked")
 	@Override
 	public  T invoke(final Object target, final Object... args) {
-		if(ArrayUtil.isEmpty(args)){
+		if (ArrayUtil.isEmpty(args)) {
 			// 默认取值
 			return (T) invokeGet(target);
-		} else if(args.length == 1){
+		} else if (args.length == 1) {
 			invokeSet(target, args[0]);
 			return null;
 		}
@@ -91,7 +117,7 @@ public class FieldInvoker implements Invoker {
 	/**
 	 * 获取字段值
 	 *
-	 * @param obj   对象,static字段则此字段为null
+	 * @param obj 对象,static字段则此字段为null
 	 * @return 字段值
 	 * @throws HutoolException 包装IllegalAccessException异常
 	 */
@@ -130,19 +156,14 @@ public class FieldInvoker implements Invoker {
 		}
 	}
 
-	@Override
-	public Class getType() {
-		return field.getType();
-	}
-
 	/**
 	 * 转换值类型
 	 *
 	 * @param value 值
 	 * @return 转换后的值
 	 */
-	private Object convertValue(final Object value){
-		if(null == converter){
+	private Object convertValue(final Object value) {
+		if (null == converter) {
 			return value;
 		}
 
diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/reflect/Invoker.java b/hutool-core/src/main/java/org/dromara/hutool/core/reflect/Invoker.java
index 6555dfa3c..a7026adeb 100644
--- a/hutool-core/src/main/java/org/dromara/hutool/core/reflect/Invoker.java
+++ b/hutool-core/src/main/java/org/dromara/hutool/core/reflect/Invoker.java
@@ -16,6 +16,8 @@
 
 package org.dromara.hutool.core.reflect;
 
+import java.lang.reflect.Type;
+
 /**
  * Invoker接口定义了调用目标对象的方法的规范。
* 它允许动态地调用方法,增强了代码的灵活性和扩展性。
@@ -36,10 +38,24 @@ public interface Invoker { T invoke(Object target, Object... args); /** - * 获取调用方法的返回类型或参数类型。 + * 获取调用方法的名称。 + * + * @return 调用方法的名称,作为字符串返回。 + */ + String getName(); + + /** + * 获取调用方法的返回类型或参数类型或字段类型。 * * @return 调用方法的返回类型,作为Class对象返回。 */ - Class getType(); + Type getType(); + + /** + * 获取调用方法的返回类型或参数类型或字段类型。 + * + * @return 调用方法的返回类型,作为Class对象返回。 + */ + Class getTypeClass(); } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/reflect/TypeUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/reflect/TypeUtil.java index dbbf5507a..9f4dc551d 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/reflect/TypeUtil.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/reflect/TypeUtil.java @@ -20,12 +20,7 @@ import org.dromara.hutool.core.array.ArrayUtil; import org.dromara.hutool.core.collection.ListUtil; import org.dromara.hutool.core.util.ObjUtil; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.lang.reflect.TypeVariable; -import java.lang.reflect.WildcardType; +import java.lang.reflect.*; import java.util.List; import java.util.Map; @@ -66,6 +61,8 @@ public class TypeUtil { if (upperBounds.length == 1) { return getClass(upperBounds[0]); } + } else if(type instanceof GenericArrayType){ + return Array.newInstance(getClass(((GenericArrayType)type).getGenericComponentType()), 0).getClass(); } } return null; diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/reflect/method/MethodInvoker.java b/hutool-core/src/main/java/org/dromara/hutool/core/reflect/method/MethodInvoker.java index 8df36673d..9b6684843 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/reflect/method/MethodInvoker.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/reflect/method/MethodInvoker.java @@ -18,12 +18,11 @@ package org.dromara.hutool.core.reflect.method; import org.dromara.hutool.core.exception.HutoolException; import org.dromara.hutool.core.lang.Assert; -import org.dromara.hutool.core.reflect.ClassUtil; -import org.dromara.hutool.core.reflect.Invoker; -import org.dromara.hutool.core.reflect.ModifierUtil; +import org.dromara.hutool.core.reflect.*; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.lang.reflect.Type; /** * 方法调用器,通过反射调用方法。 @@ -43,8 +42,10 @@ public class MethodInvoker implements Invoker { } private final Method method; - private final Class[] paramTypes; - private final Class type; + private final Type[] paramTypes; + private final Class[] paramTypeClasses; + private final Type type; + private final Class typeClass; private boolean checkArgs; /** @@ -53,17 +54,53 @@ public class MethodInvoker implements Invoker { * @param method 方法 */ public MethodInvoker(final Method method) { - this.method = method; + this.method = ReflectUtil.setAccessible(Assert.notNull(method)); - this.paramTypes = method.getParameterTypes(); + this.paramTypes = TypeUtil.getParamTypes(method); + this.paramTypeClasses = method.getParameterTypes(); if (paramTypes.length == 1) { // setter方法读取参数类型 type = paramTypes[0]; + typeClass = paramTypeClasses[0]; } else { type = method.getReturnType(); + typeClass = method.getReturnType(); } } + /** + * 获取方法 + * + * @return 方法 + */ + public Method getMethod() { + return this.method; + } + + /** + * 获取方法参数类型 + * + * @return 方法参数类型 + */ + public Type[] getParamTypes() { + return this.paramTypes; + } + + @Override + public String getName() { + return this.method.getName(); + } + + @Override + public Type getType() { + return this.type; + } + + @Override + public Class getTypeClass() { + return this.typeClass; + } + /** * 设置是否检查参数
*
@@ -81,19 +118,21 @@ public class MethodInvoker implements Invoker {
 
 	@SuppressWarnings("unchecked")
 	@Override
-	public  T invoke(Object target, final Object... args) throws HutoolException{
-		if(this.checkArgs){
+	public  T invoke(Object target, final Object... args) throws HutoolException {
+		if (this.checkArgs) {
 			checkArgs(args);
 		}
 
 		final Method method = this.method;
 		// static方法调用则target为null
-		if(ModifierUtil.isStatic(method)){
+		if (ModifierUtil.isStatic(method)) {
 			target = null;
 		}
 		// 根据方法定义的参数类型,将用户传入的参数规整和转换
 		final Object[] actualArgs = MethodUtil.actualArgs(method, args);
 		try {
+			// issue#3671 JDK15+ 修改了lambda的策略,动态生成后在metaspace不会释放,导致资源占用高
+			//return (T) LambdaUtil.buildGetter(method).apply(target);
 			return MethodHandleUtil.invokeExact(target, method, actualArgs);
 		} catch (final Exception e) {
 			// 传统反射方式执行方法
@@ -108,8 +147,8 @@ public class MethodInvoker implements Invoker {
 	/**
 	 * 执行静态方法
 	 *
-	 * @param     对象类型
-	 * @param args   参数对象
+	 * @param   对象类型
+	 * @param args 参数对象
 	 * @return 结果
 	 * @throws HutoolException 多种异常包装
 	 */
@@ -117,11 +156,6 @@ public class MethodInvoker implements Invoker {
 		return invoke(null, args);
 	}
 
-	@Override
-	public Class getType() {
-		return this.type;
-	}
-
 	/**
 	 * 检查传入参数的有效性。
 	 *
@@ -129,12 +163,12 @@ public class MethodInvoker implements Invoker {
 	 * @throws IllegalArgumentException 如果参数数组为空或长度为0,则抛出此异常。
 	 */
 	private void checkArgs(final Object[] args) {
-		final Class[] paramTypes = this.paramTypes;
+		final Class[] paramTypeClasses = this.paramTypeClasses;
 		if (null != args) {
-			Assert.isTrue(args.length == paramTypes.length, "Params length [{}] is not fit for param length [{}] of method !", args.length, paramTypes.length);
+			Assert.isTrue(args.length == paramTypeClasses.length, "Params length [{}] is not fit for param length [{}] of method !", args.length, paramTypeClasses.length);
 			Class type;
 			for (int i = 0; i < args.length; i++) {
-				type = paramTypes[i];
+				type = paramTypeClasses[i];
 				if (type.isPrimitive() && null == args[i]) {
 					// 参数是原始类型,而传入参数为null时赋予默认值
 					args[i] = ClassUtil.getDefaultValue(type);
diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/util/BooleanUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/util/BooleanUtil.java
index 00c1277d9..2072cdb0f 100644
--- a/hutool-core/src/main/java/org/dromara/hutool/core/util/BooleanUtil.java
+++ b/hutool-core/src/main/java/org/dromara/hutool/core/util/BooleanUtil.java
@@ -20,6 +20,7 @@ import org.dromara.hutool.core.array.ArrayUtil;
 import org.dromara.hutool.core.collection.set.SetUtil;
 import org.dromara.hutool.core.text.StrUtil;
 
+import java.lang.reflect.Type;
 import java.util.Set;
 
 /**
@@ -532,11 +533,11 @@ public class BooleanUtil {
 	/**
 	 * 给定类是否为Boolean或者boolean
 	 *
-	 * @param clazz 类
+	 * @param type 类
 	 * @return 是否为Boolean或者boolean
 	 * @since 4.5.2
 	 */
-	public static boolean isBoolean(final Class clazz) {
-		return (clazz == Boolean.class || clazz == boolean.class);
+	public static boolean isBoolean(final Type type) {
+		return (type == Boolean.class || type == boolean.class);
 	}
 }
diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/bean/BeanDescTest.java b/hutool-core/src/test/java/org/dromara/hutool/core/bean/BeanDescTest.java
index 3a824e3ae..e1b18b614 100644
--- a/hutool-core/src/test/java/org/dromara/hutool/core/bean/BeanDescTest.java
+++ b/hutool-core/src/test/java/org/dromara/hutool/core/bean/BeanDescTest.java
@@ -17,9 +17,12 @@
 package org.dromara.hutool.core.bean;
 
 import org.dromara.hutool.core.bean.path.AbstractBeanDesc;
+import org.dromara.hutool.core.reflect.method.MethodInvoker;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 
+import java.lang.reflect.Method;
+
 /**
  * {@link StrictBeanDesc} 单元测试类
  *
@@ -38,8 +41,10 @@ public class BeanDescTest {
 		Assertions.assertEquals("age", desc.getProp("age").getFieldName());
 		Assertions.assertEquals("getAge", desc.getGetter("age").getName());
 		Assertions.assertEquals("setAge", desc.getSetter("age").getName());
-		Assertions.assertEquals(1, desc.getSetter("age").getParameterTypes().length);
-		Assertions.assertSame(int.class, desc.getSetter("age").getParameterTypes()[0]);
+
+		final MethodInvoker setter = (MethodInvoker) desc.getSetter("age");
+		Assertions.assertEquals(1, setter.getMethod().getParameterTypes().length);
+		Assertions.assertSame(int.class, setter.getMethod().getParameterTypes()[0]);
 
 	}
 
@@ -51,8 +56,11 @@ public class BeanDescTest {
 		Assertions.assertEquals("name", prop.getFieldName());
 		Assertions.assertEquals("getName", prop.getGetter().getName());
 		Assertions.assertEquals("setName", prop.getSetter().getName());
-		Assertions.assertEquals(1, prop.getSetter().getParameterTypes().length);
-		Assertions.assertSame(String.class, prop.getSetter().getParameterTypes()[0]);
+
+		final MethodInvoker setter = (MethodInvoker) desc.getSetter("name");
+		final Method setterMethod = setter.getMethod();
+		Assertions.assertEquals(1, setterMethod.getParameterTypes().length);
+		Assertions.assertSame(String.class, setterMethod.getParameterTypes()[0]);
 	}
 
 	@Test
diff --git a/hutool-db/src/main/java/org/dromara/hutool/db/handler/row/BeanRowHandler.java b/hutool-db/src/main/java/org/dromara/hutool/db/handler/row/BeanRowHandler.java
index c35c13995..2b07f2aeb 100644
--- a/hutool-db/src/main/java/org/dromara/hutool/db/handler/row/BeanRowHandler.java
+++ b/hutool-db/src/main/java/org/dromara/hutool/db/handler/row/BeanRowHandler.java
@@ -19,12 +19,11 @@ package org.dromara.hutool.db.handler.row;
 import org.dromara.hutool.core.bean.BeanUtil;
 import org.dromara.hutool.core.bean.PropDesc;
 import org.dromara.hutool.core.reflect.ConstructorUtil;
-import org.dromara.hutool.core.reflect.TypeUtil;
-import org.dromara.hutool.core.reflect.method.MethodUtil;
+import org.dromara.hutool.core.reflect.Invoker;
+import org.dromara.hutool.core.reflect.method.MethodInvoker;
 import org.dromara.hutool.core.text.StrUtil;
 import org.dromara.hutool.db.handler.ResultSetUtil;
 
-import java.lang.reflect.Method;
 import java.sql.ResultSet;
 import java.sql.ResultSetMetaData;
 import java.sql.SQLException;
@@ -63,7 +62,7 @@ public class BeanRowHandler extends AbsRowHandler {
 		final Map propMap = BeanUtil.getBeanDesc(beanType).getPropMap(this.caseInsensitive);
 		String columnLabel;
 		PropDesc pd;
-		Method setter;
+		Invoker setter;
 		Object value;
 		for (int i = 1; i <= columnCount; i++) {
 			columnLabel = meta.getColumnLabel(i);
@@ -74,8 +73,12 @@ public class BeanRowHandler extends AbsRowHandler {
 			}
 			setter = (null == pd) ? null : pd.getSetter();
 			if (null != setter) {
-				value = ResultSetUtil.getColumnValue(rs, i, meta.getColumnType(i), TypeUtil.getFirstParamType(setter));
-				MethodUtil.invokeWithCheck(bean, setter, value);
+				value = ResultSetUtil.getColumnValue(rs, i, meta.getColumnType(i), setter.getType());
+				if(setter instanceof MethodInvoker){
+					MethodInvoker.of(((MethodInvoker) setter).getMethod()).setCheckArgs(true).invoke(bean, value);
+				}else {
+					setter.invoke(bean, value);
+				}
 			}
 		}
 		return bean;
diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/JSON.java b/hutool-json/src/main/java/org/dromara/hutool/json/JSON.java
index 652ff2dd6..71cd5eda9 100644
--- a/hutool-json/src/main/java/org/dromara/hutool/json/JSON.java
+++ b/hutool-json/src/main/java/org/dromara/hutool/json/JSON.java
@@ -20,6 +20,7 @@ import org.dromara.hutool.core.bean.path.BeanPath;
 import org.dromara.hutool.core.convert.ConvertException;
 import org.dromara.hutool.core.convert.Converter;
 import org.dromara.hutool.core.lang.mutable.MutableEntry;
+import org.dromara.hutool.core.util.ObjUtil;
 
 import java.io.Serializable;
 import java.io.StringWriter;
@@ -43,7 +44,7 @@ public interface JSON extends Converter, Cloneable, Serializable {
 	JSONConfig config();
 
 	/**
-	 * JSON大小,对于JSONObject,是键值对的多少,JSONArray则是元素的个数
+	 * JSON大小,对于JSONObject,是键值对的多少,JSONArray则是元素的个数,JSON原始数据为1
 	 *
 	 * @return 大小
 	 */
@@ -192,6 +193,6 @@ public interface JSON extends Converter, Cloneable, Serializable {
 
 	@Override
 	default Object convert(final Type targetType, final Object value) throws ConvertException {
-		return config().getConverter().convert(targetType, value);
+		return ObjUtil.defaultIfNull(config(), JSONConfig::of).getConverter().convert(targetType, value);
 	}
 }
diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/JSONPrimitive.java b/hutool-json/src/main/java/org/dromara/hutool/json/JSONPrimitive.java
new file mode 100644
index 000000000..d48ac7a1c
--- /dev/null
+++ b/hutool-json/src/main/java/org/dromara/hutool/json/JSONPrimitive.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2024 Hutool Team and hutool.cn
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.dromara.hutool.json;
+
+import org.dromara.hutool.core.lang.Assert;
+import org.dromara.hutool.core.lang.mutable.MutableEntry;
+import org.dromara.hutool.json.writer.GlobalValueWriters;
+import org.dromara.hutool.json.writer.JSONValueWriter;
+import org.dromara.hutool.json.writer.JSONWriter;
+
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.function.Predicate;
+
+/**
+ * JSON原始类型数据封装,根据RFC8259规范,JSONPrimitive只包含以下类型:
+ * 
    + *
  • number
  • + *
  • string
  • + *
  • null
  • + *
+ * + * @author Looly + * @since 6.0.0 + */ +public class JSONPrimitive implements JSON { + private static final long serialVersionUID = -2026215279191790345L; + + private Object value; + /** + * 配置项 + */ + private JSONConfig config; + + /** + * 构造 + * + * @param value 值 + */ + public JSONPrimitive(final Object value) { + this(value, null); + } + + /** + * 构造 + * + * @param value 值 + * @param config 配置项 + */ + public JSONPrimitive(final Object value, final JSONConfig config) { + this.value = Assert.notNull(value); + this.config = config; + } + + /** + * 获取值 + * + * @return 值 + */ + public Object getValue() { + return this.value; + } + + /** + * 设置值 + * + * @param value 值 + * @return this + */ + public JSONPrimitive setValue(final Object value) { + this.value = value; + return this; + } + + @Override + public JSONConfig config() { + return this.config; + } + + /** + * 设置配置项 + * + * @param config 配置项 + * @return this + */ + JSONPrimitive setConfig(final JSONConfig config) { + this.config = config; + return this; + } + + @Override + public int size() { + return 1; + } + + @Override + public Writer write(final Writer writer, final int indentFactor, final int indent, final Predicate> predicate) throws JSONException { + final Object value = this.value; + final JSONWriter jsonWriter = JSONWriter.of(writer, indentFactor, indent, config); + + // 自定义规则 + final JSONValueWriter valueWriter = GlobalValueWriters.get(value); + if (null != valueWriter) { + valueWriter.write(jsonWriter, value); + return writer; + } + + // 默认包装字符串 + jsonWriter.writeQuoteStrValue(value.toString()); + return writer; + } + + @Override + public String toString() { + final StringWriter sw = new StringWriter(); + return this.write(sw, 0, 0, null).toString(); + } +} diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/JSONUtil.java b/hutool-json/src/main/java/org/dromara/hutool/json/JSONUtil.java index 6a456475a..63a8ca4a5 100644 --- a/hutool-json/src/main/java/org/dromara/hutool/json/JSONUtil.java +++ b/hutool-json/src/main/java/org/dromara/hutool/json/JSONUtil.java @@ -16,7 +16,6 @@ package org.dromara.hutool.json; -import org.dromara.hutool.core.convert.ConvertUtil; import org.dromara.hutool.core.io.IORuntimeException; import org.dromara.hutool.core.io.file.FileUtil; import org.dromara.hutool.core.lang.Assert; @@ -161,7 +160,7 @@ public class JSONUtil { * @param obj 对象 * @return JSON */ - public static Object parse(final Object obj) { + public static JSON parse(final Object obj) { return parse(obj, null); } @@ -178,7 +177,7 @@ public class JSONUtil { * @param config JSON配置,{@code null}使用默认配置 * @return JSON(JSONObject or JSONArray) */ - public static Object parse(final Object obj, final JSONConfig config) { + public static JSON parse(final Object obj, final JSONConfig config) { if (null == config) { return JSONConverter.INSTANCE.toJSON(obj); } @@ -378,26 +377,21 @@ public class JSONUtil { * 转为实体类对象 * * @param Bean类型 - * @param json JSONObject + * @param obj JSONObject * @param config JSON配置 * @param type 实体类对象类型 * @return 实体类对象 * @since 4.3.2 */ - public static T toBean(Object json, final JSONConfig config, Type type) { - if (null == json) { + public static T toBean(final Object obj, final JSONConfig config, Type type) { + if (null == obj) { return null; } - json = parse(json, config); - if (json instanceof JSON) { - if (type instanceof TypeReference) { - type = ((TypeReference) type).getType(); - } - return ((JSON) json).toBean(type); + final JSON json = parse(obj, config); + if (type instanceof TypeReference) { + type = ((TypeReference) type).getType(); } - - //issue#I7CW27,其他类型使用默认转换 - return ConvertUtil.convert(type, json); + return json.toBean(type); } // -------------------------------------------------------------------- toBean end diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/convert/JSONConverter.java b/hutool-json/src/main/java/org/dromara/hutool/json/convert/JSONConverter.java index 7456b2074..b29cc5ff4 100644 --- a/hutool-json/src/main/java/org/dromara/hutool/json/convert/JSONConverter.java +++ b/hutool-json/src/main/java/org/dromara/hutool/json/convert/JSONConverter.java @@ -29,7 +29,6 @@ import org.dromara.hutool.core.reflect.TypeReference; import org.dromara.hutool.core.reflect.TypeUtil; import org.dromara.hutool.core.reflect.kotlin.KClassUtil; import org.dromara.hutool.core.text.StrUtil; -import org.dromara.hutool.core.util.ObjUtil; import org.dromara.hutool.json.*; import org.dromara.hutool.json.serialize.JSONDeserializer; import org.dromara.hutool.json.serialize.JSONStringer; @@ -63,6 +62,7 @@ public class JSONConverter implements Converter, Serializable { final RegisterConverter converter = RegisterConverter.getInstance(); converter.register(JSONObject.class, INSTANCE); converter.register(JSONArray.class, INSTANCE); + converter.register(JSONPrimitive.class, INSTANCE); } /** @@ -137,7 +137,7 @@ public class JSONConverter implements Converter, Serializable { * @return 转换后的对象 * @throws JSONException 转换异常 */ - public Object toJSON(Object obj) throws JSONException { + public JSON toJSON(Object obj) throws JSONException { if (null == obj) { return null; } @@ -148,10 +148,17 @@ public class JSONConverter implements Converter, Serializable { obj = ((Opt) obj).getOrNull(); } + if(obj instanceof JSON){ + return (JSON) obj; + } + + if (obj instanceof Number || obj instanceof Boolean) { + // RFC8259规范的原始类型数据 + return new JSONPrimitive(obj, config); + } + final JSON json; - if (obj instanceof JSON || obj instanceof Number || obj instanceof Boolean) { - return obj; - } else if (obj instanceof CharSequence) { + if (obj instanceof CharSequence) { return toJSON((CharSequence) obj); } else if (obj instanceof MapWrapper) { // MapWrapper实现了Iterable会被当作JSONArray,此处做修正 @@ -173,7 +180,7 @@ public class JSONConverter implements Converter, Serializable { * @return 转换后的对象 * @throws JSONException 转换异常 */ - public Object toJSON(final CharSequence str) throws JSONException { + public JSON toJSON(final CharSequence str) throws JSONException { if (null == str) { return null; } @@ -184,7 +191,7 @@ public class JSONConverter implements Converter, Serializable { // 未被包装的空串理解为null return null; } - final char firstC = jsonStr.charAt(0); + // RFC8259,JSON字符串值、number, boolean, or null final JSONParser jsonParser = JSONParser.of(new JSONTokener(jsonStr), config); final Object value = jsonParser.nextValue(); @@ -192,17 +199,10 @@ public class JSONConverter implements Converter, Serializable { // 对于用户提供的未转义字符串导致解析未结束,报错 throw new JSONException("JSON format error: {}", jsonStr); } - switch (firstC) { - case '"': - case '\'': - return InternalJSONUtil.quote((CharSequence) value); - default: - if (ObjUtil.equals(jsonStr, value)) { - // 对于直接的字符串,如abc,按照字符串处理 - return InternalJSONUtil.quote((CharSequence) value); - } - return value; + if(null == value || value instanceof JSON){ + return (JSON) value; } + return new JSONPrimitive(value, config); } // ----------------------------------------------------------- Private method start @@ -220,16 +220,16 @@ public class JSONConverter implements Converter, Serializable { */ @SuppressWarnings("unchecked") private T toBean(final Type targetType, final JSON json) { - // 自定义对象反序列化 final JSONDeserializer deserializer = InternalJSONUtil.getDeserializer(targetType); + if (null != deserializer) { return (T) deserializer.deserialize(json); } + // 当目标类型不确定时,返回原JSON final Class rawType = (Class) TypeUtil.getClass(targetType); if (null == rawType) { - // 当目标类型不确定时,返回原JSON return (T) json; //throw new JSONException("Can not get class from type: {}", targetType); } @@ -239,21 +239,29 @@ public class JSONConverter implements Converter, Serializable { return KClassUtil.newInstance(rawType, new JSONGetterValueProvider<>((JSONGetter) json)); } + final Object value; + // JSON原始类型 + if(json instanceof JSONPrimitive){ + value = ((JSONPrimitive) json).getValue(); + } else { + value = json; + } + // 标准转换器 - final Converter converter = RegisterConverter.getInstance().getConverter(targetType, json, true); + final Converter converter = RegisterConverter.getInstance().getConverter(targetType, value, true); if (null != converter) { - return (T) converter.convert(targetType, json); + return (T) converter.convert(targetType, value); } // 特殊类型转换,包括Collection、Map、强转、Array等 - final T result = (T) SpecialConverter.getInstance().convert(targetType, rawType, json); + final T result = (T) SpecialConverter.getInstance().convert(targetType, rawType, value); if (null != result) { return result; } // 尝试转Bean if (BeanUtil.isWritableBean(rawType)) { - return BeanCopier.of(json, + return BeanCopier.of(value, ConstructorUtil.newInstanceIfPossible(rawType), targetType, InternalJSONUtil.toCopyOptions(json.config())).copy(); } diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/engine/HutoolJSONEngine.java b/hutool-json/src/main/java/org/dromara/hutool/json/engine/HutoolJSONEngine.java index 15fa29184..6671a2731 100644 --- a/hutool-json/src/main/java/org/dromara/hutool/json/engine/HutoolJSONEngine.java +++ b/hutool-json/src/main/java/org/dromara/hutool/json/engine/HutoolJSONEngine.java @@ -38,14 +38,14 @@ public class HutoolJSONEngine extends AbstractJSONEngine { @Override public void serialize(final Object bean, final Writer writer) { initEngine(); - final JSON json = (JSON) JSONUtil.parse(bean, this.hutoolSJONConfig); + final JSON json = JSONUtil.parse(bean, this.hutoolSJONConfig); json.write(writer, ObjUtil.defaultIfNull(this.config, JSONEngineConfig::isPrettyPrint, false) ? 2 : 0, 0, null); } @Override public T deserialize(final Reader reader, final Object type) { initEngine(); - final JSON json = (JSON) JSONUtil.parse(reader, this.hutoolSJONConfig); + final JSON json = JSONUtil.parse(reader, this.hutoolSJONConfig); return json.toBean((Type) type); } diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/engine/gson/GsonEngine.java b/hutool-json/src/main/java/org/dromara/hutool/json/engine/gson/GsonEngine.java index ffad6697f..4d8582f25 100644 --- a/hutool-json/src/main/java/org/dromara/hutool/json/engine/gson/GsonEngine.java +++ b/hutool-json/src/main/java/org/dromara/hutool/json/engine/gson/GsonEngine.java @@ -19,6 +19,7 @@ package org.dromara.hutool.json.engine.gson; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import org.dromara.hutool.core.lang.Assert; +import org.dromara.hutool.core.lang.wrapper.Wrapper; import org.dromara.hutool.core.util.ObjUtil; import org.dromara.hutool.json.JSONException; import org.dromara.hutool.json.engine.AbstractJSONEngine; @@ -31,6 +32,7 @@ import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.util.Date; +import java.util.TimeZone; /** * Gson引擎实现 @@ -38,7 +40,7 @@ import java.util.Date; * @author Looly * @since 6.0.0 */ -public class GsonEngine extends AbstractJSONEngine { +public class GsonEngine extends AbstractJSONEngine implements Wrapper { private Gson gson; @@ -51,6 +53,12 @@ public class GsonEngine extends AbstractJSONEngine { Assert.notNull(Gson.class); } + @Override + public Gson getRaw() { + initEngine(); + return this.gson; + } + @Override public void serialize(final Object bean, final Writer writer) { initEngine(); @@ -106,7 +114,11 @@ public class GsonEngine extends AbstractJSONEngine { * @param dateFormat 日期格式 */ private void registerDate(final GsonBuilder builder, final String dateFormat){ - builder.registerTypeAdapter(Date.class, new DateSerDesc(dateFormat)); + // java date + builder.registerTypeHierarchyAdapter(Date.class, new DateSerDesc(dateFormat)); + builder.registerTypeHierarchyAdapter(TimeZone.class, TimeZoneSerDesc.INSTANCE); + + // java.time builder.registerTypeAdapter(LocalDateTime.class, new TemporalSerDesc(LocalDateTime.class, dateFormat)); builder.registerTypeAdapter(LocalDate.class, new TemporalSerDesc(LocalDate.class, dateFormat)); builder.registerTypeAdapter(LocalTime.class, new TemporalSerDesc(LocalTime.class, dateFormat)); diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/engine/gson/TimeZoneSerDesc.java b/hutool-json/src/main/java/org/dromara/hutool/json/engine/gson/TimeZoneSerDesc.java new file mode 100644 index 000000000..31543a0eb --- /dev/null +++ b/hutool-json/src/main/java/org/dromara/hutool/json/engine/gson/TimeZoneSerDesc.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2024 Hutool Team and hutool.cn + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.dromara.hutool.json.engine.gson; + +import com.google.gson.*; + +import java.lang.reflect.Type; +import java.util.TimeZone; + +/** + * 时区序列化描述 + * + * @author Looly + * @since 6.0.0 + */ +public class TimeZoneSerDesc implements GsonSerDesc{ + + /** + * 默认时区格式化描述 + */ + public static final TimeZoneSerDesc INSTANCE = new TimeZoneSerDesc(); + + @Override + public JsonElement serialize(final TimeZone src, final Type typeOfSrc, final JsonSerializationContext context) { + return new JsonPrimitive(src.getID()); + } + + @Override + public TimeZone deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context) throws JsonParseException { + return TimeZone.getTimeZone(json.getAsString()); + } +} diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/engine/jackson/JacksonEngine.java b/hutool-json/src/main/java/org/dromara/hutool/json/engine/jackson/JacksonEngine.java index b74768921..1fd4dd2ec 100644 --- a/hutool-json/src/main/java/org/dromara/hutool/json/engine/jackson/JacksonEngine.java +++ b/hutool-json/src/main/java/org/dromara/hutool/json/engine/jackson/JacksonEngine.java @@ -26,6 +26,7 @@ import com.fasterxml.jackson.databind.SerializationFeature; import org.dromara.hutool.core.date.DateUtil; import org.dromara.hutool.core.io.IORuntimeException; import org.dromara.hutool.core.lang.Assert; +import org.dromara.hutool.core.lang.wrapper.Wrapper; import org.dromara.hutool.core.reflect.ConstructorUtil; import org.dromara.hutool.core.text.StrUtil; import org.dromara.hutool.core.util.ObjUtil; @@ -42,7 +43,7 @@ import java.io.Writer; * * @author Looly */ -public class JacksonEngine extends AbstractJSONEngine { +public class JacksonEngine extends AbstractJSONEngine implements Wrapper { private ObjectMapper mapper; @@ -60,7 +61,8 @@ public class JacksonEngine extends AbstractJSONEngine { * * @return {@link ObjectMapper}对象 */ - public ObjectMapper getMapper() { + @Override + public ObjectMapper getRaw() { initEngine(); return mapper; } diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/serialize/JSONPrimitiveSerializer.java b/hutool-json/src/main/java/org/dromara/hutool/json/serialize/JSONPrimitiveSerializer.java new file mode 100644 index 000000000..d172dd7f6 --- /dev/null +++ b/hutool-json/src/main/java/org/dromara/hutool/json/serialize/JSONPrimitiveSerializer.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2013-2024 Hutool Team and hutool.cn + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.dromara.hutool.json.serialize; + +import org.dromara.hutool.json.JSONPrimitive; + +/** + * 原始对象的序列化接口,用于将特定对象序列化为{@link JSONPrimitive} + * + * @param 对象类型 + * @author Looly + */ +@FunctionalInterface +public interface JSONPrimitiveSerializer extends JSONSerializer { +} diff --git a/hutool-json/src/test/java/org/dromara/hutool/json/IssueI6LBZATest.java b/hutool-json/src/test/java/org/dromara/hutool/json/IssueI6LBZATest.java index 68ffab167..2e5d659a9 100644 --- a/hutool-json/src/test/java/org/dromara/hutool/json/IssueI6LBZATest.java +++ b/hutool-json/src/test/java/org/dromara/hutool/json/IssueI6LBZATest.java @@ -16,36 +16,41 @@ package org.dromara.hutool.json; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; + public class IssueI6LBZATest { @Test public void parseJSONStringTest() { final String a = "\"a\""; final Object parse = JSONUtil.parse(a); - Assertions.assertEquals(String.class, parse.getClass()); + assertEquals(JSONPrimitive.class, parse.getClass()); + assertEquals(a, parse.toString()); } @Test public void parseJSONStringTest2() { final String a = "'a'"; final Object parse = JSONUtil.parse(a); - Assertions.assertEquals(String.class, parse.getClass()); + assertEquals(JSONPrimitive.class, parse.getClass()); + assertEquals("\"a\"", parse.toString()); } @Test public void parseJSONErrorTest() { final String a = "a"; final Object parse = JSONUtil.parse(a); - Assertions.assertEquals(String.class, parse.getClass()); - Assertions.assertEquals("\"a\"", parse); + assertEquals(JSONPrimitive.class, parse.getClass()); + assertEquals("\"a\"", parse.toString()); } @Test public void parseJSONNumberTest() { final String a = "123"; - final Object parse = JSONUtil.parse(a); - Assertions.assertEquals(Integer.class, parse.getClass()); + final JSON parse = JSONUtil.parse(a); + assertEquals(JSONPrimitive.class, parse.getClass()); + assertEquals(123, ((JSONPrimitive)parse).getValue()); + assertEquals(Integer.class, ((JSONPrimitive)parse).getValue().getClass()); } } diff --git a/hutool-json/src/test/java/org/dromara/hutool/json/IssueI7M2GZTest.java b/hutool-json/src/test/java/org/dromara/hutool/json/IssueI7M2GZTest.java index d0b8d9c08..0d541d7be 100755 --- a/hutool-json/src/test/java/org/dromara/hutool/json/IssueI7M2GZTest.java +++ b/hutool-json/src/test/java/org/dromara/hutool/json/IssueI7M2GZTest.java @@ -59,8 +59,8 @@ public class IssueI7M2GZTest { entity.setList(list); final String json = JSONUtil.toJsonStr(entity); - //Console.log(json); final MyEntity result = JSONUtil.toBean(json, new TypeReference>() {}); + Assertions.assertEquals("new Object", result.getList().get(0).getName()); Assertions.assertNotNull(result.getList().get(0).getParsed()); Assertions.assertEquals(Integer.valueOf(12), result.getList().get(0).getParsed()); diff --git a/hutool-json/src/test/java/org/dromara/hutool/json/JSONUtilTest.java b/hutool-json/src/test/java/org/dromara/hutool/json/JSONUtilTest.java index 8c9ea3b41..463a12cb0 100644 --- a/hutool-json/src/test/java/org/dromara/hutool/json/JSONUtilTest.java +++ b/hutool-json/src/test/java/org/dromara/hutool/json/JSONUtilTest.java @@ -53,29 +53,29 @@ public class JSONUtilTest { void parseEmptyValue() { // https://www.rfc-editor.org/rfc/rfc8259#section-7 // 未被包装的空串理解为null - Object parse = JSONUtil.parse(""); + JSON parse = JSONUtil.parse(""); assertNull(parse); parse = JSONUtil.parse("\"\""); - assertEquals("\"\"", parse); + assertEquals("\"\"", parse.toString()); } @Test public void parseValueTest() { - Object parse = JSONUtil.parse(123); - assertEquals(123, parse); + JSON parse = JSONUtil.parse(123); + assertEquals(123, ((JSONPrimitive)parse).getValue()); parse = JSONUtil.parse("\"abc\""); - assertEquals("\"abc\"", parse); + assertEquals("\"abc\"", parse.toString()); parse = JSONUtil.parse("\"\\\"bc\""); - assertEquals("\"\\\"bc\"", parse); + assertEquals("\"\\\"bc\"", parse.toString()); parse = JSONUtil.parse("true"); - assertEquals(true, parse); + assertEquals(true, ((JSONPrimitive)parse).getValue()); parse = JSONUtil.parse("False"); - assertEquals(false, parse); + assertEquals(false, ((JSONPrimitive)parse).getValue()); parse = JSONUtil.parse("null"); assertNull(parse); diff --git a/hutool-json/src/test/java/org/dromara/hutool/json/engine/JSONEngineTest.java b/hutool-json/src/test/java/org/dromara/hutool/json/engine/JSONEngineTest.java index 4d7c573c2..5602b998a 100644 --- a/hutool-json/src/test/java/org/dromara/hutool/json/engine/JSONEngineTest.java +++ b/hutool-json/src/test/java/org/dromara/hutool/json/engine/JSONEngineTest.java @@ -23,6 +23,7 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import java.util.Arrays; +import java.util.TimeZone; public class JSONEngineTest { @@ -43,6 +44,12 @@ public class JSONEngineTest { Arrays.stream(engineNames).forEach(this::assertWriteLocalDateFormat); } + @Test + void writeTimeZoneTest() { + // TODO Hutool无法序列化TimeZone等特殊对象 + Arrays.stream(engineNames).forEach(this::assertWriteTimeZone); + } + private void assertWriteDateFormat(final String engineName) { final DateTime date = DateUtil.parse("2024-01-01 01:12:21"); final BeanWithDate bean = new BeanWithDate(date, TimeUtil.of(date)); @@ -78,4 +85,16 @@ public class JSONEngineTest { jsonString = engine.toJsonString(bean); Assertions.assertEquals("{\"date1\":null,\"date2\":null}", jsonString); } + + private void assertWriteTimeZone(final String engineName) { + final TimeZone timeZone = TimeZone.getTimeZone("GMT+08:00"); + final JSONEngine engine = JSONEngineFactory.createEngine(engineName); + + String jsonString = engine.toJsonString(timeZone); + Assertions.assertEquals("\"GMT+08:00\"", jsonString); + + engine.init(JSONEngineConfig.of().setIgnoreNullValue(false)); + jsonString = engine.toJsonString(timeZone); + Assertions.assertEquals("\"GMT+08:00\"", jsonString); + } }