diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/bean/copier/ValueProviderToBeanCopier.java b/hutool-core/src/main/java/org/dromara/hutool/core/bean/copier/ValueProviderToBeanCopier.java index b976fc406..a886edf3f 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/bean/copier/ValueProviderToBeanCopier.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/bean/copier/ValueProviderToBeanCopier.java @@ -62,19 +62,19 @@ public class ValueProviderToBeanCopier extends AbsCopier targetPropDescMap = getBeanDesc(actualEditable).getPropMap(copyOptions.ignoreCase); - targetPropDescMap.forEach((tFieldName, tDesc) -> { + targetPropDescMap.forEach((tFieldName, propDesc) -> { if (null == tFieldName) { return; } // 检查目标字段可写性 - if (null == tDesc || !tDesc.isWritable(this.copyOptions.transientSupport)) { + if (null == propDesc || !propDesc.isWritable(this.copyOptions.transientSupport)) { // 字段不可写,跳过之 return; } // 获取目标字段真实类型 - final Type fieldType = TypeUtil.getActualType(this.targetType ,tDesc.getFieldType()); + final Type fieldType = TypeUtil.getActualType(this.targetType ,propDesc.getFieldType()); // 编辑键值对 final MutableEntry entry = copyOptions.editField(tFieldName, null); if(null == entry){ @@ -92,12 +92,12 @@ public class ValueProviderToBeanCopier extends AbsCopier targetClass, final Object value) { // Handle Date if (value instanceof Date) { - return CalendarUtil.calendar((Date)value); + return CalendarUtil.calendar((Date) value); } // Handle Long if (value instanceof Long) { //此处使用自动拆装箱 - return CalendarUtil.calendar((Long)value); + return CalendarUtil.calendar((Long) value); } - if(value instanceof XMLGregorianCalendar){ + if (value instanceof XMLGregorianCalendar) { return CalendarUtil.calendar((XMLGregorianCalendar) value); } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/DateConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/DateConverter.java index a38a7a152..87f02c392 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/DateConverter.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/DateConverter.java @@ -57,7 +57,7 @@ public class DateConverter extends AbstractConverter implements MatcherConverter /** * 构造 * - * @param format 日期格式 + * @param format 日期格式,{@code null}表示无格式定义 */ public DateConverter(final String format) { this.format = format; 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 9f4dc551d..e7cf82c12 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 @@ -63,6 +63,8 @@ public class TypeUtil { } } else if(type instanceof GenericArrayType){ return Array.newInstance(getClass(((GenericArrayType)type).getGenericComponentType()), 0).getClass(); + } else if(type instanceof TypeReference){ + return getClass(((TypeReference)type).getType()); } } return null; @@ -429,6 +431,7 @@ public class TypeUtil { } if (typeVariable instanceof TypeVariable) { + // TODO TypeReference无效 return ActualTypeMapperPool.getActualType(type, (TypeVariable) typeVariable); } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/util/EnumUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/util/EnumUtil.java index 439a199f3..f84ce1a4b 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/util/EnumUtil.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/util/EnumUtil.java @@ -17,14 +17,16 @@ package org.dromara.hutool.core.util; import org.dromara.hutool.core.collection.CollUtil; -import org.dromara.hutool.core.lang.Assert; import org.dromara.hutool.core.func.LambdaUtil; import org.dromara.hutool.core.func.SerFunction; +import org.dromara.hutool.core.lang.Assert; import org.dromara.hutool.core.map.MapUtil; import org.dromara.hutool.core.reflect.FieldUtil; +import org.dromara.hutool.core.reflect.TypeUtil; import org.dromara.hutool.core.text.StrUtil; import java.lang.reflect.Field; +import java.lang.reflect.Type; import java.util.*; import java.util.function.Function; import java.util.function.Predicate; @@ -40,11 +42,11 @@ public class EnumUtil { /** * 指定类是否为Enum类 * - * @param clazz 类 + * @param type 类 * @return 是否为Enum类 */ - public static boolean isEnum(final Class clazz) { - return Assert.notNull(clazz).isEnum(); + public static boolean isEnum(final Type type) { + return Assert.notNull(TypeUtil.getClass(type)).isEnum(); } /** diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/util/TypeUtilTest.java b/hutool-core/src/test/java/org/dromara/hutool/core/util/TypeUtilTest.java index 56ec14912..ac2e49063 100644 --- a/hutool-core/src/test/java/org/dromara/hutool/core/util/TypeUtilTest.java +++ b/hutool-core/src/test/java/org/dromara/hutool/core/util/TypeUtilTest.java @@ -16,10 +16,11 @@ package org.dromara.hutool.core.util; -import org.dromara.hutool.core.reflect.FieldUtil; -import org.dromara.hutool.core.reflect.method.MethodUtil; -import org.dromara.hutool.core.reflect.TypeUtil; import lombok.Data; +import org.dromara.hutool.core.reflect.FieldUtil; +import org.dromara.hutool.core.reflect.TypeReference; +import org.dromara.hutool.core.reflect.TypeUtil; +import org.dromara.hutool.core.reflect.method.MethodUtil; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -27,9 +28,16 @@ import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; +import java.util.Map; public class TypeUtilTest { + @Test + void getMapClassTest() { + final Class aClass = TypeUtil.getClass(new TypeReference>() {}); + Assertions.assertEquals(Map.class, aClass); + } + @Test public void getEleTypeTest() { final Method method = MethodUtil.getMethod(TestClass.class, "getList"); diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/InternalJSONUtil.java b/hutool-json/src/main/java/org/dromara/hutool/json/InternalJSONUtil.java index e19000360..a698cfdc5 100644 --- a/hutool-json/src/main/java/org/dromara/hutool/json/InternalJSONUtil.java +++ b/hutool-json/src/main/java/org/dromara/hutool/json/InternalJSONUtil.java @@ -28,6 +28,7 @@ import org.dromara.hutool.core.text.CharUtil; import org.dromara.hutool.core.text.StrUtil; import org.dromara.hutool.core.text.split.SplitUtil; import org.dromara.hutool.core.util.ObjUtil; +import org.dromara.hutool.json.mapper.JSONValueMapper; import org.dromara.hutool.json.reader.JSONTokener; import java.io.IOException; @@ -178,8 +179,7 @@ public final class InternalJSONUtil { .setIgnoreError(config.isIgnoreError()) .setIgnoreNullValue(config.isIgnoreNullValue()) .setTransientSupport(config.isTransientSupport()) - // 使用JSON转换器 - .setConverter(config.getConverter()); + .setConverter((targetType, value) -> JSONValueMapper.of(config, null).map(value)); } /** 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 9557a13d6..f4c8949bd 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 @@ -17,10 +17,9 @@ package org.dromara.hutool.json; 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 org.dromara.hutool.json.serializer.JSONDeserializer; +import org.dromara.hutool.json.serializer.TypeAdapterManager; import org.dromara.hutool.json.writer.JSONWriter; import java.io.Serializable; @@ -38,7 +37,7 @@ import java.util.function.Predicate; * * @author Looly */ -public interface JSON extends Converter, Serializable { +public interface JSON extends Serializable { /** * 获取JSON配置 @@ -64,6 +63,33 @@ public interface JSON extends Converter, Serializable { return 0 == size(); } + /** + * 转为JSONObject + * + * @return JSONObject + */ + default JSONObject asJSONObject() { + return (JSONObject) this; + } + + /** + * 转为JSONArray + * + * @return JSONArray + */ + default JSONArray asJSONArray() { + return (JSONArray) this; + } + + /** + * 转为JSONPrimitive + * + * @return JSONPrimitive + */ + default JSONPrimitive asJSONPrimitive() { + return (JSONPrimitive) this; + } + /** * 通过表达式获取JSON中嵌套的对象
*
    @@ -141,7 +167,13 @@ public interface JSON extends Converter, Serializable { */ @SuppressWarnings("unchecked") default T getByPath(final String expression, final Type resultType) { - return (T) config().getConverter().convert(resultType, getByPath(expression)); + final JSON json = getByPath(expression); + if (null == json) { + return null; + } + + final JSONDeserializer deserializer = TypeAdapterManager.getInstance().getDeserializer(json, resultType); + return (T) deserializer.deserialize(json, resultType); } /** @@ -199,11 +231,10 @@ public interface JSON extends Converter, Serializable { */ @SuppressWarnings("unchecked") default T toBean(final Type type) { - return (T) convert(type, this); - } - - @Override - default Object convert(final Type targetType, final Object value) throws ConvertException { - return ObjUtil.defaultIfNull(config(), JSONConfig::of).getConverter().convert(targetType, value); + final JSONDeserializer deserializer = TypeAdapterManager.getInstance().getDeserializer(this, type); + if(null == deserializer){ + throw new JSONException("No deserializer for type: " + type); + } + return (T) deserializer.deserialize(this, type); } } diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/JSONArray.java b/hutool-json/src/main/java/org/dromara/hutool/json/JSONArray.java index c93e9dbec..64ab1d7da 100644 --- a/hutool-json/src/main/java/org/dromara/hutool/json/JSONArray.java +++ b/hutool-json/src/main/java/org/dromara/hutool/json/JSONArray.java @@ -23,6 +23,7 @@ import org.dromara.hutool.core.convert.impl.ArrayConverter; import org.dromara.hutool.core.lang.Validator; import org.dromara.hutool.core.lang.mutable.MutableEntry; import org.dromara.hutool.core.util.ObjUtil; +import org.dromara.hutool.json.mapper.JSONValueMapper; import org.dromara.hutool.json.writer.JSONWriter; import java.util.*; @@ -51,6 +52,7 @@ public class JSONArray extends ListWrapper implements JSON, JSONGetter implements JSON, JSONGetter(initialCapacity)); this.config = ObjUtil.defaultIfNull(config, JSONConfig::of); + this.mapper = JSONValueMapper.of(config, null); } // endregion @@ -129,7 +132,7 @@ public class JSONArray extends ListWrapper implements JSON, JSONGetter implements JSON, JSONGetter extends TypeGetter { return (null == jsonArray) ? null : jsonArray.toList(beanType); } + @SuppressWarnings("unchecked") @Override default T get(final K key, final Type type, final T defaultValue) { - final Object value = this.getObj(key); + Object value = this.getObj(key); if (ObjUtil.isNull(value)) { return defaultValue; } - return get(key, type, config().getConverter(), defaultValue); + if(value instanceof JSON){ + final JSONDeserializer deserializer = TypeAdapterManager.getInstance().getDeserializer((JSON) value, type); + if(null == deserializer){ + throw new JSONException("No deserializer for type: " + type); + } + value = deserializer.deserialize((JSON) value, type); + return null == value ? defaultValue : (T) value; + } + + // JSONPrimitive中的值 + return CompositeConverter.getInstance().convert(type, value, defaultValue); } } diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/JSONObject.java b/hutool-json/src/main/java/org/dromara/hutool/json/JSONObject.java index 716dad05e..b6eba81f3 100644 --- a/hutool-json/src/main/java/org/dromara/hutool/json/JSONObject.java +++ b/hutool-json/src/main/java/org/dromara/hutool/json/JSONObject.java @@ -25,6 +25,7 @@ import org.dromara.hutool.core.map.MapUtil; import org.dromara.hutool.core.map.MapWrapper; import org.dromara.hutool.core.text.StrUtil; import org.dromara.hutool.core.util.ObjUtil; +import org.dromara.hutool.json.mapper.JSONValueMapper; import org.dromara.hutool.json.writer.JSONWriter; import java.util.Arrays; @@ -52,6 +53,7 @@ public class JSONObject extends MapWrapper implements JSON, JSONGe * 配置项 */ private final JSONConfig config; + private final JSONValueMapper mapper; /** * 构造,初始容量为 {@link #DEFAULT_CAPACITY},KEY有序 @@ -79,6 +81,7 @@ public class JSONObject extends MapWrapper implements JSON, JSONGe public JSONObject(final int capacity, final JSONConfig config) { super(InternalJSONUtil.createRawMap(capacity, config)); this.config = ObjUtil.defaultIfNull(config, JSONConfig::of); + this.mapper = JSONValueMapper.of(config, null); } @Override @@ -218,7 +221,7 @@ public class JSONObject extends MapWrapper implements JSON, JSONGe * @throws JSONException 值是无穷数字抛出此异常 */ public JSONObject set(final String key, final Object value) throws JSONException { - this.put(key, this.config.getConverter().convert(JSON.class, value)); + this.put(key, this.mapper.map(value)); return this; } 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 index 245a5421b..924115f4d 100644 --- a/hutool-json/src/main/java/org/dromara/hutool/json/JSONPrimitive.java +++ b/hutool-json/src/main/java/org/dromara/hutool/json/JSONPrimitive.java @@ -18,11 +18,10 @@ package org.dromara.hutool.json; import org.dromara.hutool.core.lang.Assert; import org.dromara.hutool.core.lang.wrapper.Wrapper; +import org.dromara.hutool.core.math.NumberUtil; import org.dromara.hutool.json.writer.JSONWriter; -import org.dromara.hutool.json.writer.ValueWriter; -import org.dromara.hutool.json.writer.ValueWriterManager; +import org.dromara.hutool.json.writer.NumberWriteMode; -import java.lang.reflect.Type; import java.math.BigDecimal; import java.math.BigInteger; @@ -40,6 +39,21 @@ import java.math.BigInteger; public class JSONPrimitive implements Wrapper, JSON { private static final long serialVersionUID = -2026215279191790345L; + /** + * JS中表示的数字最大值 + */ + private static final long JS_MAX_NUMBER = 9007199254740992L; + + /** + * 判断给定对象是否可以转为JSONPrimitive类型 + * + * @param value 值 + * @return 是否为JSONPrimitive类型 + */ + public static boolean isTypeForJSONPrimitive(final Object value) { + return value instanceof Boolean || value instanceof Number || value instanceof String; + } + private Object value; /** * 配置项 @@ -161,22 +175,18 @@ public class JSONPrimitive implements Wrapper, JSON { return 1; } - @Override - public T convert(final Type targetType, final Object value, final T defaultValue) { - return JSON.super.convert(targetType, this.value, defaultValue); - } - @Override public void write(final JSONWriter writer) throws JSONException { - // 自定义规则 - final ValueWriter valueWriter = ValueWriterManager.getInstance().get(value); - if (null != valueWriter) { - valueWriter.write(writer, value); - return; + if(value instanceof Boolean){ + // Boolean + writer.writeRaw(value.toString()); + } else if (value instanceof Number){ + // Number + writeNumber(writer, (Number) value); + } else{ + // 默认包装字符串 + writer.writeQuoteStrValue(value.toString()); } - - // 默认包装字符串 - writer.writeQuoteStrValue(value.toString()); } @Override @@ -185,4 +195,36 @@ public class JSONPrimitive implements Wrapper, JSON { write(jsonWriter); return jsonWriter.toString(); } + + /** + * 写出数字,根据{@link JSONConfig#isStripTrailingZeros()} 配置不同,写出不同数字
    + * 主要针对Double型是否去掉小数点后多余的0
    + * 此方法输出的值不包装引号。 + * + * @param writer {@link JSONWriter} + * @param number 数字 + */ + private void writeNumber(final JSONWriter writer, final Number number) { + final JSONConfig config = writer.getConfig(); + // since 5.6.2可配置是否去除末尾多余0,例如如果为true,5.0返回5 + final boolean isStripTrailingZeros = (null == config) || config.isStripTrailingZeros(); + final String numberStr = NumberUtil.toStr(number, isStripTrailingZeros); + + final NumberWriteMode numberWriteMode = (null == config) ? NumberWriteMode.NORMAL : config.getNumberWriteMode(); + switch (numberWriteMode){ + case JS: + if(number.longValue() > JS_MAX_NUMBER){ + writer.writeQuoteStrValue(numberStr); + } else{ + writer.writeRaw(numberStr); + } + break; + case STRING: + writer.writeQuoteStrValue(numberStr); + break; + default: + writer.writeRaw(numberStr); + break; + } + } } 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 8a74f23b4..088c2fa3c 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 @@ -25,12 +25,9 @@ import org.dromara.hutool.core.text.StrUtil; import org.dromara.hutool.core.util.ObjUtil; import org.dromara.hutool.json.mapper.JSONValueMapper; import org.dromara.hutool.json.writer.JSONWriter; -import org.dromara.hutool.json.writer.ValueWriter; -import org.dromara.hutool.json.writer.ValueWriterManager; import org.dromara.hutool.json.xml.JSONXMLUtil; import java.io.File; -import java.io.StringWriter; import java.io.Writer; import java.lang.reflect.Type; import java.nio.charset.Charset; @@ -301,16 +298,6 @@ public class JSONUtil { * @since 5.7.12 */ public static String toJsonStr(final Object obj, final JSONConfig jsonConfig) { - // 自定义规则,优先级高于全局规则 - final ValueWriter valueWriter = ValueWriterManager.getInstance().get(obj); - if (null != valueWriter) { - final StringWriter stringWriter = new StringWriter(); - final JSONWriter jsonWriter = JSONWriter.of(stringWriter, 0, 0, null); - // 用户对象自定义实现了JSONValueWriter接口,理解为需要自定义输出 - valueWriter.write(jsonWriter, obj); - return stringWriter.toString(); - } - if (null == obj) { return null; } 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 a26b50058..30441b34e 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 @@ -31,7 +31,7 @@ import org.dromara.hutool.json.mapper.JSONValueMapper; import org.dromara.hutool.json.reader.JSONParser; import org.dromara.hutool.json.reader.JSONTokener; import org.dromara.hutool.json.serializer.JSONDeserializer; -import org.dromara.hutool.json.serializer.SerializerManager; +import org.dromara.hutool.json.serializer.TypeAdapterManager; import java.io.Serializable; import java.lang.reflect.Type; @@ -181,7 +181,7 @@ public class JSONConverter implements Converter, Serializable { @SuppressWarnings("unchecked") private T toBean(final Type targetType, final JSON json) { // 自定义对象反序列化 - final JSONDeserializer deserializer = SerializerManager.getInstance().getDeserializer(json, targetType); + final JSONDeserializer deserializer = TypeAdapterManager.getInstance().getDeserializer(json, targetType); if (null != deserializer) { return (T) deserializer.deserialize(json, targetType); } diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/mapper/JSONValueMapper.java b/hutool-json/src/main/java/org/dromara/hutool/json/mapper/JSONValueMapper.java index 37d796c1a..40fccf753 100644 --- a/hutool-json/src/main/java/org/dromara/hutool/json/mapper/JSONValueMapper.java +++ b/hutool-json/src/main/java/org/dromara/hutool/json/mapper/JSONValueMapper.java @@ -17,18 +17,17 @@ package org.dromara.hutool.json.mapper; import org.dromara.hutool.core.array.ArrayUtil; -import org.dromara.hutool.core.exception.ExceptionUtil; import org.dromara.hutool.core.lang.Opt; import org.dromara.hutool.core.lang.mutable.MutableEntry; import org.dromara.hutool.core.map.MapWrapper; import org.dromara.hutool.core.text.StrUtil; +import org.dromara.hutool.core.util.ObjUtil; import org.dromara.hutool.json.*; import org.dromara.hutool.json.reader.JSONParser; import org.dromara.hutool.json.reader.JSONTokener; import org.dromara.hutool.json.serializer.JSONSerializer; -import org.dromara.hutool.json.serializer.SerializerManager; import org.dromara.hutool.json.serializer.SimpleJSONContext; -import org.dromara.hutool.json.writer.ValueWriterManager; +import org.dromara.hutool.json.serializer.TypeAdapterManager; import org.dromara.hutool.json.xml.JSONXMLParser; import org.dromara.hutool.json.xml.ParseConfig; @@ -78,7 +77,7 @@ public class JSONValueMapper implements Serializable { * @param predicate 键值对过滤编辑器,可以通过实现此接口,完成解析前对键值对的过滤和修改操作,{@link Predicate#test(Object)}为{@code true}保留 */ public JSONValueMapper(final JSONConfig jsonConfig, final Predicate> predicate) { - this.jsonConfig = jsonConfig; + this.jsonConfig = ObjUtil.defaultIfNull(jsonConfig, JSONConfig::of); this.predicate = predicate; } @@ -154,13 +153,14 @@ public class JSONValueMapper implements Serializable { } // 自定义序列化 - final JSONSerializer serializer = SerializerManager.getInstance().getSerializer(obj); + final JSONSerializer serializer = TypeAdapterManager.getInstance() + .getSerializer(obj, obj.getClass()); if (null != serializer) { return serializer.serialize(obj, new SimpleJSONContext(null, this.jsonConfig)); } // 原始类型 - if (null != ValueWriterManager.getInstance().get(obj)) { + if (JSONPrimitive.isTypeForJSONPrimitive(obj)) { return new JSONPrimitive(obj, jsonConfig); } @@ -183,7 +183,7 @@ public class JSONValueMapper implements Serializable { if (jsonConfig.isIgnoreError()) { return null; } - throw ExceptionUtil.wrap(exception, JSONException.class); + throw exception instanceof JSONException ? (JSONException) exception : new JSONException(exception); } } diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/serializer/JSONContext.java b/hutool-json/src/main/java/org/dromara/hutool/json/serializer/JSONContext.java index 656e11505..5c3db7bca 100644 --- a/hutool-json/src/main/java/org/dromara/hutool/json/serializer/JSONContext.java +++ b/hutool-json/src/main/java/org/dromara/hutool/json/serializer/JSONContext.java @@ -16,6 +16,7 @@ package org.dromara.hutool.json.serializer; +import org.dromara.hutool.core.util.ObjUtil; import org.dromara.hutool.json.JSON; import org.dromara.hutool.json.JSONConfig; @@ -40,6 +41,6 @@ public interface JSONContext { * @return JSON配置 */ default JSONConfig config() { - return getContextJson().config(); + return ObjUtil.apply(getContextJson(), JSON::config); } } diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/serializer/JSONDeserializer.java b/hutool-json/src/main/java/org/dromara/hutool/json/serializer/JSONDeserializer.java index f93294a09..4da9d2909 100644 --- a/hutool-json/src/main/java/org/dromara/hutool/json/serializer/JSONDeserializer.java +++ b/hutool-json/src/main/java/org/dromara/hutool/json/serializer/JSONDeserializer.java @@ -31,7 +31,7 @@ import java.lang.reflect.Type; * @author Looly */ @FunctionalInterface -public interface JSONDeserializer { +public interface JSONDeserializer extends TypeAdapter{ /** * 反序列化,通过实现此方法,自定义实现JSON转换为指定类型的逻辑
    diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/serializer/JSONSerializer.java b/hutool-json/src/main/java/org/dromara/hutool/json/serializer/JSONSerializer.java index d5307a60f..dd3c33c8a 100644 --- a/hutool-json/src/main/java/org/dromara/hutool/json/serializer/JSONSerializer.java +++ b/hutool-json/src/main/java/org/dromara/hutool/json/serializer/JSONSerializer.java @@ -27,7 +27,7 @@ import org.dromara.hutool.json.JSONObject; * @author Looly */ @FunctionalInterface -public interface JSONSerializer { +public interface JSONSerializer extends TypeAdapter{ /** * 序列化实现,通过实现此方法,将指定类型的对象转换为{@link JSON}对象,可以: diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/writer/impl/package-info.java b/hutool-json/src/main/java/org/dromara/hutool/json/serializer/TypeAdapter.java similarity index 79% rename from hutool-json/src/main/java/org/dromara/hutool/json/writer/impl/package-info.java rename to hutool-json/src/main/java/org/dromara/hutool/json/serializer/TypeAdapter.java index 0e60c95a4..92ca0aea9 100644 --- a/hutool-json/src/main/java/org/dromara/hutool/json/writer/impl/package-info.java +++ b/hutool-json/src/main/java/org/dromara/hutool/json/serializer/TypeAdapter.java @@ -14,9 +14,11 @@ * limitations under the License. */ +package org.dromara.hutool.json.serializer; + /** - * {@link org.dromara.hutool.json.writer.ValueWriter} 实现 + * JSON类型适配器,实现此接口即同时实现对象的序列化和反序列化 * * @author Looly */ -package org.dromara.hutool.json.writer.impl; +public interface TypeAdapter{} diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/serializer/SerializerManager.java b/hutool-json/src/main/java/org/dromara/hutool/json/serializer/TypeAdapterManager.java similarity index 57% rename from hutool-json/src/main/java/org/dromara/hutool/json/serializer/SerializerManager.java rename to hutool-json/src/main/java/org/dromara/hutool/json/serializer/TypeAdapterManager.java index 2d2b5398f..785182636 100644 --- a/hutool-json/src/main/java/org/dromara/hutool/json/serializer/SerializerManager.java +++ b/hutool-json/src/main/java/org/dromara/hutool/json/serializer/TypeAdapterManager.java @@ -19,39 +19,37 @@ package org.dromara.hutool.json.serializer; import org.dromara.hutool.core.collection.CollUtil; import org.dromara.hutool.core.collection.set.ConcurrentHashSet; import org.dromara.hutool.core.lang.Assert; +import org.dromara.hutool.core.map.MapUtil; import org.dromara.hutool.core.map.concurrent.SafeConcurrentHashMap; import org.dromara.hutool.core.reflect.ConstructorUtil; import org.dromara.hutool.core.reflect.TypeUtil; import org.dromara.hutool.json.JSON; +import org.dromara.hutool.json.JSONException; import org.dromara.hutool.json.serializer.impl.*; import java.lang.reflect.Type; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.LocalTime; import java.util.Map; import java.util.Set; -import java.util.TimeZone; /** * JSON序列化和反序列化管理器,用于管理JSON序列化器,注册和注销自定义序列化器和反序列化器。
    * 此管理器管理着两种类型的序列化器和反序列化器: *
      - *
    • 类型精准匹配方式。通过Java对象类型匹配,只会匹配查找的类型,而不匹配子类。可以调用{@link #register(Type, JSONSerializer)} 和 {@link #register(Type, JSONDeserializer)}注册。
    • - *
    • 匹配器(Matcher)方式。通过判断序列化和反序列化器中match方法,找到自定义的序列化和反序列化器,可以调用{@link #register(MatcherJSONSerializer)} 和 {@link #register(MatcherJSONDeserializer)}注册。
    • + *
    • 类型精准匹配方式。通过Java对象类型匹配,只会匹配查找的类型,而不匹配子类。可以调用{@link #register(Type, TypeAdapter)}注册。
    • + *
    • 匹配器(Matcher)方式。通过判断序列化和反序列化器中match方法,找到自定义的序列化和反序列化器,可以调用{@link #register(TypeAdapter)}注册。
    • *
    - * + *

    * 管理器的使用分为三种方式: *

      - *
    • 全局模式: 使用{@link SerializerManager#getInstance()}调用单例,全局可用。
    • - *
    • 实例模式: 使用{@link SerializerManager#of()}创建实例,局部可用。
    • + *
    • 全局模式: 使用{@link TypeAdapterManager#getInstance()}调用单例,全局可用。
    • + *
    • 实例模式: 使用{@link TypeAdapterManager#of()}创建实例,局部可用。
    • *
    • 自定义模式:使用{@code new SerializerManager()}创建实例,不加载默认的转换器。
    • *
    * * @author looly * @since 6.0.0 */ -public class SerializerManager { +public class TypeAdapterManager { /** * 类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例 没有绑定关系,而且只有被调用到才会装载,从而实现了延迟加载 */ @@ -59,7 +57,8 @@ public class SerializerManager { /** * 静态初始化器,由JVM来保证线程安全 */ - private static final SerializerManager INSTANCE = new SerializerManager(); + private static final TypeAdapterManager INSTANCE = new TypeAdapterManager(); + static { registerDefault(INSTANCE); } @@ -70,8 +69,8 @@ public class SerializerManager { * * @return SerializerManager */ - public static SerializerManager getInstance() { - return SerializerManager.SingletonHolder.INSTANCE; + public static TypeAdapterManager getInstance() { + return SingletonHolder.INSTANCE; } /** @@ -79,10 +78,10 @@ public class SerializerManager { * * @return SerializerManager */ - public static SerializerManager of() { - final SerializerManager serializerManager = new SerializerManager(); - registerDefault(serializerManager); - return serializerManager; + public static TypeAdapterManager of() { + final TypeAdapterManager typeAdapterManager = new TypeAdapterManager(); + registerDefault(typeAdapterManager); + return typeAdapterManager; } /** @@ -107,73 +106,54 @@ public class SerializerManager { /** * 构造 */ - public SerializerManager() { + public TypeAdapterManager() { } // region ----- register /** - * 注册自定义序列化器,用于自定义对象序列化
    - * 当按照匹配规则匹配时,使用对应的序列化器进行序列化 + * 注册自定义类型适配器,用于自定义对象序列化和反序列化
    + * 提供的适配器必须为实现{@link MatcherJSONSerializer}或{@link MatcherJSONDeserializer}接口
    + * 当两个接口都实现时,同时注册序列化和反序列化器 * - * @param serializer 自定义序列化器 + * @param typeAdapter 自定义类型适配器 * @return this */ - public SerializerManager register(final MatcherJSONSerializer serializer) { - if (null != serializer) { - getSerializerSet().add(serializer); + public TypeAdapterManager register(final TypeAdapter typeAdapter) { + Assert.notNull(typeAdapter, "typeAdapter must be not null!"); + if(typeAdapter instanceof MatcherJSONSerializer || typeAdapter instanceof MatcherJSONDeserializer){ + if(typeAdapter instanceof MatcherJSONSerializer){ + getSerializerSet().add((MatcherJSONSerializer) typeAdapter); + } + if(typeAdapter instanceof MatcherJSONDeserializer){ + getDeserializerSet().add((MatcherJSONDeserializer) typeAdapter); + } + return this; } - return this; + + throw new JSONException("Adapter: {} is not MatcherJSONSerializer or MatcherJSONDeserializer", typeAdapter.getClass()); } /** - * 注册自定义序列化器,用于自定义对象序列化
    - * 当类型精准匹配时,使用对应的序列化器进行序列化 + * 注册自定义类型适配器,用于自定义对象序列化和反序列化 * * @param type 类型 - * @param serializer 自定义序列化器,{@code null}表示移除 + * @param typeAdapter 自定义序列化器,{@code null}表示移除 * @return this */ - public SerializerManager register(final Type type, final JSONSerializer serializer) { + public TypeAdapterManager register(final Type type, final TypeAdapter typeAdapter) { Assert.notNull(type); - if (null == serializer) { - getSerializerMap().remove(type); - } else { - getSerializerMap().put(type, serializer); + if(typeAdapter instanceof JSONSerializer || typeAdapter instanceof JSONDeserializer){ + if(typeAdapter instanceof JSONSerializer){ + getSerializerMap().put(type, (JSONSerializer) typeAdapter); + } + if(typeAdapter instanceof JSONDeserializer){ + getDeserializerMap().put(type, (JSONDeserializer) typeAdapter); + } + return this; } - return this; - } - /** - * 注册自定义反序列化器,用于自定义对象反序列化
    - * 当按照匹配规则匹配时,使用对应的反序列化器进行反序列化 - * - * @param deserializer 自定义反序列化器 - * @return this - */ - public SerializerManager register(final MatcherJSONDeserializer deserializer) { - if (null != deserializer) { - getDeserializerSet().add(deserializer); - } - return this; - } - - /** - * 注册自定义反序列化器,用于自定义对象反序列化
    - * 当类型精准匹配时,使用对应的反序列化器进行反序列化 - * - * @param type 类型,{@code null}表示 - * @param deserializer 自定义反序列化器,{@code null}表示移除 - * @return this - */ - public SerializerManager register(final Type type, final JSONDeserializer deserializer) { - Assert.notNull(type); - if (null == deserializer) { - getDeserializerMap().remove(type); - } else { - getDeserializerMap().put(type, deserializer); - } - return this; + throw new JSONException("Adapter: {} is not JSONSerializer or JSONDeserializer", typeAdapter.getClass()); } // endregion @@ -183,60 +163,53 @@ public class SerializerManager { * 获取匹配器对应的序列化器 * * @param bean 对象 - * @return JSONSerializer - */ - @SuppressWarnings({"unchecked"}) - public MatcherJSONSerializer getSerializer(final Object bean) { - if(CollUtil.isNotEmpty(this.serializerSet)){ - for (final MatcherJSONSerializer serializer : this.serializerSet) { - if (serializer.match(bean, null)) { - return (MatcherJSONSerializer) serializer; - } - } - } - return null; - } - - /** - * 获取匹配器对应的序列化器 - * * @param type 类型 * @return JSONSerializer */ - @SuppressWarnings("unchecked") - public JSONSerializer getSerializer(final Type type) { - if(null == type || CollUtil.isEmpty(this.serializerMap)){ - return null; + @SuppressWarnings({"unchecked"}) + public JSONSerializer getSerializer(final Object bean, final Type type) { + JSONSerializer result = null; + if (null != type && MapUtil.isNotEmpty(this.serializerMap)) { + result = (JSONSerializer) this.serializerMap.get(type); } - return (JSONSerializer) this.serializerMap.get(type); + + if (null == result && CollUtil.isNotEmpty(this.serializerSet)) { + for (final MatcherJSONSerializer serializer : this.serializerSet) { + if (serializer.match(bean, null)) { + result = (MatcherJSONSerializer) serializer; + break; + } + } + } + return result; } /** * 获取匹配器对应的反序列化器 * - * @param json JSON - * @param type 类型 - * @return JSONDeserializer + * @param json JSON, 单独查找强类型匹配传{@code null} + * @param type 类型, 单独查匹配器传{@code null} + * @return JSONDeserializer,始终非空 */ @SuppressWarnings("unchecked") public JSONDeserializer getDeserializer(final JSON json, final Type type) { final Class rawType = TypeUtil.getClass(type); - if(null == rawType){ + if (null == rawType) { return null; } if (JSONDeserializer.class.isAssignableFrom(rawType)) { return (JSONDeserializer) ConstructorUtil.newInstanceIfPossible(rawType); } - if(CollUtil.isNotEmpty(this.deserializerMap)){ + if (CollUtil.isNotEmpty(this.deserializerMap)) { final JSONDeserializer jsonDeserializer = this.deserializerMap.get(type); - if(null != jsonDeserializer){ + if (null != jsonDeserializer) { return (JSONDeserializer) jsonDeserializer; } } // Matcher - if(CollUtil.isNotEmpty(this.deserializerSet)){ + if (CollUtil.isNotEmpty(this.deserializerSet)) { for (final MatcherJSONDeserializer deserializer : this.deserializerSet) { if (deserializer.match(json, type)) { return (JSONDeserializer) deserializer; @@ -244,7 +217,7 @@ public class SerializerManager { } } - return null; + return DefaultDeserializer.INSTANCE; } // endregion @@ -292,32 +265,30 @@ public class SerializerManager { } return this.deserializerMap; } + // endregion /** * 注册默认的序列化器和反序列化器 + * * @param manager {@code SerializerManager} */ - private static void registerDefault(final SerializerManager manager) { - manager.register(LocalDate.class, (JSONSerializer) new TemporalAccessorSerializer(LocalDate.class)); - manager.register(LocalDate.class, (JSONDeserializer) new TemporalAccessorSerializer(LocalDate.class)); - - manager.register(LocalTime.class, (JSONSerializer) new TemporalAccessorSerializer(LocalTime.class)); - manager.register(LocalTime.class, (JSONDeserializer) new TemporalAccessorSerializer(LocalTime.class)); - - manager.register(LocalDateTime.class, (JSONSerializer) new TemporalAccessorSerializer(LocalDateTime.class)); - manager.register(LocalDateTime.class, (JSONDeserializer) new TemporalAccessorSerializer(LocalDateTime.class)); - - manager.register((MatcherJSONSerializer) TimeZoneSerializer.INSTANCE); - manager.register((MatcherJSONDeserializer) TimeZoneSerializer.INSTANCE); - + private static void registerDefault(final TypeAdapterManager manager) { // issue#I5WDP0 对于Kotlin对象,由于参数可能非空限制,导致无法创建一个默认的对象再赋值 manager.register(KBeanDeserializer.INSTANCE); - manager.register(CollectionDeserializer.INSTANCE); manager.register(ArrayDeserializer.INSTANCE); manager.register(MapDeserializer.INSTANCE); manager.register(EntryDeserializer.INSTANCE); manager.register(RecordDeserializer.INSTANCE); + + + manager.register(DateTypeAdapter.INSTANCE); + manager.register(CalendarTypeAdapter.INSTANCE); + manager.register(TemporalTypeAdapter.INSTANCE); + manager.register(TimeZoneTypeAdapter.INSTANCE); + manager.register(EnumTypeAdapter.INSTANCE); + manager.register(ThrowableTypeAdapter.INSTANCE); + // 最低优先级 + manager.register(BeanTypeAdapter.INSTANCE); } - // endregion } diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/BeanTypeAdapter.java b/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/BeanTypeAdapter.java new file mode 100644 index 000000000..138bfc0fa --- /dev/null +++ b/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/BeanTypeAdapter.java @@ -0,0 +1,85 @@ +/* + * 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.serializer.impl; + +import org.dromara.hutool.core.bean.BeanUtil; +import org.dromara.hutool.core.bean.copier.BeanToMapCopier; +import org.dromara.hutool.core.bean.copier.ValueProviderToBeanCopier; +import org.dromara.hutool.core.lang.copier.Copier; +import org.dromara.hutool.core.reflect.ConstructorUtil; +import org.dromara.hutool.core.reflect.TypeUtil; +import org.dromara.hutool.core.util.ObjUtil; +import org.dromara.hutool.json.InternalJSONUtil; +import org.dromara.hutool.json.JSON; +import org.dromara.hutool.json.JSONObject; +import org.dromara.hutool.json.serializer.JSONContext; +import org.dromara.hutool.json.serializer.MatcherJSONDeserializer; +import org.dromara.hutool.json.serializer.MatcherJSONSerializer; + +import java.lang.reflect.Type; + +/** + * Bean对象适配器,将Bean对象序列化为JSONObject,反序列化为Bean对象 + * + * @author looly + * @since 6.0.0 + */ +public class BeanTypeAdapter implements MatcherJSONSerializer, MatcherJSONDeserializer { + + /** + * 单例 + */ + public static final BeanTypeAdapter INSTANCE = new BeanTypeAdapter(); + + @Override + public boolean match(final Object bean, final JSONContext context) { + final JSON contextJson = ObjUtil.apply(context, JSONContext::getContextJson); + return BeanUtil.isReadableBean(bean.getClass()) + && (null == contextJson || contextJson instanceof JSONObject); + } + + @Override + public boolean match(final JSON json, final Type deserializeType) { + return json instanceof JSONObject && BeanUtil.isWritableBean(TypeUtil.getClass(deserializeType)); + } + + @Override + public JSON serialize(final Object bean, final JSONContext context) { + JSONObject contextJson = (JSONObject) ObjUtil.apply(context, JSONContext::getContextJson); + if(null == contextJson){ + contextJson = new JSONObject(context.config()); + } + + final BeanToMapCopier copier = new BeanToMapCopier( + bean, + contextJson, + JSONObject.class, InternalJSONUtil.toCopyOptions(context.config()) + ); + return (JSON) copier.copy(); + } + + @Override + public Object deserialize(final JSON json, final Type deserializeType) { + final Copier copier = new ValueProviderToBeanCopier<>( + new JSONObjectValueProvider((JSONObject) json), + ConstructorUtil.newInstanceIfPossible(TypeUtil.getClass(deserializeType)), + deserializeType, + InternalJSONUtil.toCopyOptions(json.config()) + ); + return copier.copy(); + } +} diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/CalendarTypeAdapter.java b/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/CalendarTypeAdapter.java new file mode 100644 index 000000000..b6ddfde9e --- /dev/null +++ b/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/CalendarTypeAdapter.java @@ -0,0 +1,72 @@ +/* + * 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.serializer.impl; + +import org.dromara.hutool.core.convert.impl.CalendarConverter; +import org.dromara.hutool.core.date.DateUtil; +import org.dromara.hutool.core.reflect.TypeUtil; +import org.dromara.hutool.core.util.ObjUtil; +import org.dromara.hutool.json.JSON; +import org.dromara.hutool.json.JSONConfig; +import org.dromara.hutool.json.JSONPrimitive; +import org.dromara.hutool.json.serializer.JSONContext; +import org.dromara.hutool.json.serializer.MatcherJSONDeserializer; +import org.dromara.hutool.json.serializer.MatcherJSONSerializer; + +import java.lang.reflect.Type; +import java.util.Calendar; + +/** + * 日期类型适配器,用于将日期对象转换为给定格式或时间戳 + * + * @author looly + * @since 6.0.0 + */ +public class CalendarTypeAdapter implements MatcherJSONSerializer, MatcherJSONDeserializer { + + /** + * 单例 + */ + public static final CalendarTypeAdapter INSTANCE = new CalendarTypeAdapter(); + + @Override + public boolean match(final Object bean, final JSONContext context) { + return bean instanceof Calendar; + } + + @Override + public boolean match(final JSON json, final Type deserializeType) { + return Calendar.class.isAssignableFrom(TypeUtil.getClass(deserializeType)); + } + + @Override + public JSON serialize(final Calendar bean, final JSONContext context) { + final JSONConfig config = ObjUtil.apply(context, JSONContext::config); + final String format = ObjUtil.apply(config, JSONConfig::getDateFormat); + return new JSONPrimitive( + null == format + ? bean.getTimeInMillis() + : DateUtil.format(DateUtil.date(bean), format), config); + } + + @Override + public Calendar deserialize(final JSON json, final Type deserializeType) { + final JSONConfig config = json.config(); + final String format = ObjUtil.apply(config, JSONConfig::getDateFormat); + return (Calendar) new CalendarConverter(format).convert(deserializeType, json.asJSONPrimitive().getValue()); + } +} diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/DateTypeAdapter.java b/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/DateTypeAdapter.java new file mode 100644 index 000000000..94afaf0fd --- /dev/null +++ b/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/DateTypeAdapter.java @@ -0,0 +1,72 @@ +/* + * 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.serializer.impl; + +import org.dromara.hutool.core.convert.impl.DateConverter; +import org.dromara.hutool.core.date.DateUtil; +import org.dromara.hutool.core.reflect.TypeUtil; +import org.dromara.hutool.core.util.ObjUtil; +import org.dromara.hutool.json.JSON; +import org.dromara.hutool.json.JSONConfig; +import org.dromara.hutool.json.JSONPrimitive; +import org.dromara.hutool.json.serializer.JSONContext; +import org.dromara.hutool.json.serializer.MatcherJSONDeserializer; +import org.dromara.hutool.json.serializer.MatcherJSONSerializer; + +import java.lang.reflect.Type; +import java.util.Date; + +/** + * 日期类型适配器,用于将日期对象转换为给定格式或时间戳 + * + * @author looly + * @since 6.0.0 + */ +public class DateTypeAdapter implements MatcherJSONSerializer, MatcherJSONDeserializer { + + /** + * 单例 + */ + public static final DateTypeAdapter INSTANCE = new DateTypeAdapter(); + + @Override + public boolean match(final Object bean, final JSONContext context) { + return bean instanceof Date; + } + + @Override + public boolean match(final JSON json, final Type deserializeType) { + return Date.class.isAssignableFrom(TypeUtil.getClass(deserializeType)); + } + + @Override + public JSON serialize(final Date bean, final JSONContext context) { + final JSONConfig config = ObjUtil.apply(context, JSONContext::config); + final String format = ObjUtil.apply(config, JSONConfig::getDateFormat); + return new JSONPrimitive( + null == format + ? bean.getTime() + : DateUtil.format(bean, format), config); + } + + @Override + public Date deserialize(final JSON json, final Type deserializeType) { + final JSONConfig config = json.config(); + final String format = ObjUtil.apply(config, JSONConfig::getDateFormat); + return (Date) new DateConverter(format).convert(deserializeType, json.asJSONPrimitive().getValue()); + } +} diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/DefaultDeserializer.java b/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/DefaultDeserializer.java index 8c21e0323..3b62d485b 100644 --- a/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/DefaultDeserializer.java +++ b/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/DefaultDeserializer.java @@ -16,14 +16,10 @@ package org.dromara.hutool.json.serializer.impl; -import org.dromara.hutool.core.bean.copier.ValueProviderToBeanCopier; import org.dromara.hutool.core.convert.CompositeConverter; -import org.dromara.hutool.core.lang.copier.Copier; -import org.dromara.hutool.core.reflect.ConstructorUtil; import org.dromara.hutool.core.reflect.TypeUtil; import org.dromara.hutool.json.*; import org.dromara.hutool.json.serializer.JSONDeserializer; -import org.dromara.hutool.json.serializer.JSONObjectValueProvider; import java.lang.reflect.Type; @@ -34,6 +30,12 @@ import java.lang.reflect.Type; * @since 6.0.0 */ public class DefaultDeserializer implements JSONDeserializer { + + /** + * 单例 + */ + public static final DefaultDeserializer INSTANCE = new DefaultDeserializer(); + @Override public Object deserialize(final JSON json, final Type deserializeType) { // 当目标类型不确定时,返回原JSON @@ -61,14 +63,7 @@ public class DefaultDeserializer implements JSONDeserializer { * @return 反序列化后的对象 */ private Object fromJSONObject(final JSONObject json, final Type deserializeType, final Class rawType) { - // 转为POJO - final Copier copier = new ValueProviderToBeanCopier<>( - new JSONObjectValueProvider(json), - ConstructorUtil.newInstanceIfPossible(rawType), - deserializeType, - InternalJSONUtil.toCopyOptions(json.config()) - ); - return copier.copy(); + throw new JSONException("Unsupported JSONObject to {}", rawType); } /** @@ -80,7 +75,7 @@ public class DefaultDeserializer implements JSONDeserializer { * @return 反序列化后的对象 */ private Object fromJSONArray(final JSONArray json, final Type deserializeType, final Class rawType) { - return json; + throw new JSONException("Unsupported JSONArray to {}", rawType); } /** diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/EnumTypeAdapter.java b/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/EnumTypeAdapter.java new file mode 100644 index 000000000..7b870cef8 --- /dev/null +++ b/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/EnumTypeAdapter.java @@ -0,0 +1,63 @@ +/* + * 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.serializer.impl; + +import org.dromara.hutool.core.reflect.TypeUtil; +import org.dromara.hutool.core.util.EnumUtil; +import org.dromara.hutool.core.util.ObjUtil; +import org.dromara.hutool.json.JSON; +import org.dromara.hutool.json.JSONPrimitive; +import org.dromara.hutool.json.serializer.JSONContext; +import org.dromara.hutool.json.serializer.MatcherJSONDeserializer; +import org.dromara.hutool.json.serializer.MatcherJSONSerializer; + +import java.lang.reflect.Type; + +/** + * 枚举类型适配器,将枚举转换为字符串,反序列化时将字符串转为枚举对象 + * + * @author Looly + */ +public class EnumTypeAdapter implements MatcherJSONSerializer, MatcherJSONDeserializer { + + /** + * 单例 + */ + public static final EnumTypeAdapter INSTANCE = new EnumTypeAdapter(); + + @Override + public boolean match(final Object bean, final JSONContext context) { + return EnumUtil.isEnum(bean); + } + + @Override + public boolean match(final JSON json, final Type deserializeType) { + return EnumUtil.isEnum(deserializeType); + } + + @Override + public JSON serialize(final Object bean, final JSONContext context) { + return new JSONPrimitive(((Enum) bean).name(), + ObjUtil.apply(context, JSONContext::config)); + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + @Override + public Object deserialize(final JSON json, final Type deserializeType) { + return EnumUtil.fromString((Class) TypeUtil.getClass(deserializeType), (String) json.asJSONPrimitive().getValue()); + } +} diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/serializer/JSONObjectValueProvider.java b/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/JSONObjectValueProvider.java similarity index 96% rename from hutool-json/src/main/java/org/dromara/hutool/json/serializer/JSONObjectValueProvider.java rename to hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/JSONObjectValueProvider.java index 5e8171e47..e6b59759e 100644 --- a/hutool-json/src/main/java/org/dromara/hutool/json/serializer/JSONObjectValueProvider.java +++ b/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/JSONObjectValueProvider.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.dromara.hutool.json.serializer; +package org.dromara.hutool.json.serializer.impl; import org.dromara.hutool.core.bean.copier.ValueProvider; import org.dromara.hutool.json.JSON; diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/RecordDeserializer.java b/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/RecordDeserializer.java index fbf3600ff..71a2b960a 100644 --- a/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/RecordDeserializer.java +++ b/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/RecordDeserializer.java @@ -20,7 +20,6 @@ import org.dromara.hutool.core.bean.RecordUtil; import org.dromara.hutool.core.reflect.TypeUtil; import org.dromara.hutool.json.JSON; import org.dromara.hutool.json.JSONObject; -import org.dromara.hutool.json.serializer.JSONObjectValueProvider; import org.dromara.hutool.json.serializer.MatcherJSONDeserializer; import java.lang.reflect.Type; diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/TemporalAccessorSerializer.java b/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/TemporalTypeAdapter.java similarity index 81% rename from hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/TemporalAccessorSerializer.java rename to hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/TemporalTypeAdapter.java index a237cae63..f2902bc6b 100644 --- a/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/TemporalAccessorSerializer.java +++ b/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/TemporalTypeAdapter.java @@ -20,10 +20,11 @@ import org.dromara.hutool.core.convert.impl.TemporalAccessorConverter; import org.dromara.hutool.core.lang.Assert; import org.dromara.hutool.core.lang.Opt; import org.dromara.hutool.core.math.NumberUtil; +import org.dromara.hutool.core.reflect.TypeUtil; import org.dromara.hutool.json.*; import org.dromara.hutool.json.serializer.JSONContext; -import org.dromara.hutool.json.serializer.JSONDeserializer; -import org.dromara.hutool.json.serializer.JSONSerializer; +import org.dromara.hutool.json.serializer.MatcherJSONDeserializer; +import org.dromara.hutool.json.serializer.MatcherJSONSerializer; import java.lang.reflect.Type; import java.time.LocalDate; @@ -41,9 +42,8 @@ import java.time.temporal.TemporalAccessor; * * * @author looly - * @since 5.7.22 */ -public class TemporalAccessorSerializer implements JSONSerializer, JSONDeserializer { +public class TemporalTypeAdapter implements MatcherJSONSerializer, MatcherJSONDeserializer { private static final String YEAR_KEY = "year"; private static final String MONTH_KEY = "month"; @@ -53,15 +53,19 @@ public class TemporalAccessorSerializer implements JSONSerializer temporalAccessorClass; - /** - * 构造 - * - * @param temporalAccessorClass TemporalAccessor实现类型 + * 单例 */ - public TemporalAccessorSerializer(final Class temporalAccessorClass) { - this.temporalAccessorClass = temporalAccessorClass; + public static final TemporalTypeAdapter INSTANCE = new TemporalTypeAdapter(); + + @Override + public boolean match(final JSON json, final Type deserializeType) { + return TemporalAccessor.class.isAssignableFrom(TypeUtil.getClass(deserializeType)); + } + + @Override + public boolean match(final Object bean, final JSONContext context) { + return bean instanceof TemporalAccessor; } @Override @@ -111,9 +115,10 @@ public class TemporalAccessorSerializer implements JSONSerializer temporalAccessorClass = TypeUtil.getClass(deserializeType); // JSONObject final JSONObject jsonObject = (JSONObject) json; - if (LocalDate.class.equals(this.temporalAccessorClass) || LocalDateTime.class.equals(this.temporalAccessorClass)) { + if (LocalDate.class.equals(temporalAccessorClass) || LocalDateTime.class.equals(temporalAccessorClass)) { // 年 final Integer year = jsonObject.getInt(YEAR_KEY); Assert.notNull(year, "Field 'year' must be not null"); @@ -135,7 +140,7 @@ public class TemporalAccessorSerializer implements JSONSerializer, MatcherJSONDeserializer { + + /** + * 单例 + */ + public static final ThrowableTypeAdapter INSTANCE = new ThrowableTypeAdapter(); + + @Override + public boolean match(final Object bean, final JSONContext context) { + return bean instanceof Throwable; + } + + @Override + public boolean match(final JSON json, final Type deserializeType) { + return Throwable.class.isAssignableFrom(TypeUtil.getClass(deserializeType)); + } + + @Override + public JSON serialize(final Throwable bean, final JSONContext context) { + return new JSONPrimitive( + StrUtil.format("{}: {}", bean.getClass().getName(), bean.getMessage()), + ObjUtil.apply(context, JSONContext::config)); + } + + @Override + public Throwable deserialize(final JSON json, final Type deserializeType) { + final String value = (String) json.asJSONPrimitive().getValue(); + return (Throwable) ConstructorUtil.newInstance(TypeUtil.getClass(deserializeType), + StrUtil.subAfter(value, ": ", false)); + } +} diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/TimeZoneSerializer.java b/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/TimeZoneTypeAdapter.java similarity index 88% rename from hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/TimeZoneSerializer.java rename to hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/TimeZoneTypeAdapter.java index b62d47f1a..b2a135e01 100644 --- a/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/TimeZoneSerializer.java +++ b/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/TimeZoneTypeAdapter.java @@ -27,17 +27,17 @@ import java.lang.reflect.Type; import java.util.TimeZone; /** - * 时区序列化器 + * 时区类型适配器 * * @author looly * @since 6.0.0 */ -public class TimeZoneSerializer implements MatcherJSONSerializer, MatcherJSONDeserializer { +public class TimeZoneTypeAdapter implements MatcherJSONSerializer, MatcherJSONDeserializer { /** * 单例 */ - public static final TimeZoneSerializer INSTANCE = new TimeZoneSerializer(); + public static final TimeZoneTypeAdapter INSTANCE = new TimeZoneTypeAdapter(); @Override public boolean match(final JSON json, final Type deserializeType) { diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/serializer/package-info.java b/hutool-json/src/main/java/org/dromara/hutool/json/serializer/package-info.java index 0ee71e92b..667479adc 100644 --- a/hutool-json/src/main/java/org/dromara/hutool/json/serializer/package-info.java +++ b/hutool-json/src/main/java/org/dromara/hutool/json/serializer/package-info.java @@ -27,5 +27,16 @@ *
  1. 序列化(Serialize) 指:【Java对象】 转换为 【JSON对象】
  2. *
  3. 反序列化(Deserialize)指:【JSON对象】 转换为 【Java对象】
  4. * + * + * 3. JSON序列化实现: + *
      + *
    • TypeAdapter:类型适配器,标记序列化或反序列化
    • + *
    • JSONSerializer:JSON序列化接口,用于自定义序列化
    • + *
    • JSONDeserializer:JSON反序列化接口,用于自定义反序列化
    • + *
    + * + * 4. JSON序列化管理:
    + * TypeAdapterManager用于管理定义的序列化和反序列化器 + * */ package org.dromara.hutool.json.serializer; diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/writer/JSONWriter.java b/hutool-json/src/main/java/org/dromara/hutool/json/writer/JSONWriter.java index ccad73471..f876f2ba7 100644 --- a/hutool-json/src/main/java/org/dromara/hutool/json/writer/JSONWriter.java +++ b/hutool-json/src/main/java/org/dromara/hutool/json/writer/JSONWriter.java @@ -372,13 +372,6 @@ public class JSONWriter implements Appendable, Flushable, Closeable { */ @SuppressWarnings("resource") private JSONWriter writeObjValue(final Object value) { - // 自定义规则 - final ValueWriter valueWriter = ValueWriterManager.getInstance().get(value); - if (null != valueWriter) { - valueWriter.write(this, value); - return this; - } - // 默认规则 if (value == null) { writeRaw(StrUtil.NULL); diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/writer/ValueWriterManager.java b/hutool-json/src/main/java/org/dromara/hutool/json/writer/ValueWriterManager.java deleted file mode 100644 index 09625d146..000000000 --- a/hutool-json/src/main/java/org/dromara/hutool/json/writer/ValueWriterManager.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * 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.writer; - -import org.dromara.hutool.core.lang.Assert; -import org.dromara.hutool.json.writer.impl.*; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * ValueWriter管理器,用于管理ValueWriter,提供ValueWriter的注册和获取 - * - * @author looly - * @since 5.8.0 - */ -public class ValueWriterManager { - - /** - * 类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例 没有绑定关系,而且只有被调用到才会装载,从而实现了延迟加载 - */ - private static class SingletonHolder { - /** - * 静态初始化器,由JVM来保证线程安全 - */ - private static final ValueWriterManager INSTANCE; - static { - INSTANCE = new ValueWriterManager(); - registerDefault(); - } - } - - /** - * 获得单例的 ValueWriterManager - * - * @return ValueWriterManager - */ - public static ValueWriterManager getInstance() { - return ValueWriterManager.SingletonHolder.INSTANCE; - } - - private final List valueWriterList; - - /** - * 构造 - */ - public ValueWriterManager() { - this.valueWriterList = Collections.synchronizedList(new ArrayList<>(6)); - } - - /** - * 加入自定义的对象值写出规则 - * - * @param valueWriter 自定义对象写出实现 - */ - public void register(final ValueWriter valueWriter) { - valueWriterList.add(Assert.notNull(valueWriter)); - } - - /** - * 获取自定义对象值写出规则,后加入的优先 - * - * @param value 值,{@code null}表示需要自定义null的输出 - * @return 自定义的 {@link ValueWriter} - */ - public ValueWriter get(final Object value) { - if (value instanceof ValueWriter) { - return (ValueWriter) value; - } - - final List valueWriterList = this.valueWriterList; - ValueWriter valueWriter; - for (int i = valueWriterList.size() - 1 ; i >= 0 ; i--) { - valueWriter = valueWriterList.get(i); - if (valueWriter.test(value)) { - return valueWriter; - } - } - return null; - } - - /** - * 注册默认的ValueWriter - */ - private static void registerDefault() { - final ValueWriterManager manager = SingletonHolder.INSTANCE; - // JDK对象最后判断 - manager.register(JdkValueWriter.INSTANCE); - manager.register(NumberValueWriter.INSTANCE); - manager.register(DateValueWriter.INSTANCE); - manager.register(BooleanValueWriter.INSTANCE); - } -} diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/writer/impl/BooleanValueWriter.java b/hutool-json/src/main/java/org/dromara/hutool/json/writer/impl/BooleanValueWriter.java deleted file mode 100644 index ad0aeeaf6..000000000 --- a/hutool-json/src/main/java/org/dromara/hutool/json/writer/impl/BooleanValueWriter.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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.writer.impl; - -import org.dromara.hutool.json.writer.JSONWriter; -import org.dromara.hutool.json.writer.ValueWriter; - -/** - * Boolean类型的值写出器 - * - * @author looly - * @since 6.0.0 - */ -public class BooleanValueWriter implements ValueWriter { - /** - * 单例对象 - */ - public static final BooleanValueWriter INSTANCE = new BooleanValueWriter(); - - @Override - public boolean test(final Object bool) { - return bool instanceof Boolean; - } - - @Override - public void write(final JSONWriter writer, final Object bool) { - writer.writeRaw(bool.toString()); - } -} diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/writer/impl/DateValueWriter.java b/hutool-json/src/main/java/org/dromara/hutool/json/writer/impl/DateValueWriter.java deleted file mode 100644 index fac574590..000000000 --- a/hutool-json/src/main/java/org/dromara/hutool/json/writer/impl/DateValueWriter.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * 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.writer.impl; - -import org.dromara.hutool.core.convert.ConvertUtil; -import org.dromara.hutool.core.date.DateUtil; -import org.dromara.hutool.core.date.TemporalAccessorUtil; -import org.dromara.hutool.core.date.format.GlobalCustomFormat; -import org.dromara.hutool.core.text.StrUtil; -import org.dromara.hutool.json.InternalJSONUtil; -import org.dromara.hutool.json.writer.JSONWriter; -import org.dromara.hutool.json.writer.ValueWriter; - -import java.time.MonthDay; -import java.time.temporal.TemporalAccessor; -import java.util.Calendar; -import java.util.Date; - -/** - * 日期类型的值写出器
    - * 支持包括:{@link Date}、{@link Calendar}、{@link TemporalAccessor} - * - * @author looly - * @since 6.0.0 - */ -public class DateValueWriter implements ValueWriter { - /** - * 单例对象 - */ - public static final DateValueWriter INSTANCE = new DateValueWriter(); - - @Override - public boolean test(final Object value) { - return value instanceof Date || value instanceof Calendar || value instanceof TemporalAccessor; - } - - @Override - public void write(final JSONWriter writer, final Object value) { - final String rawString; - // issue#2572@Github - if (value instanceof MonthDay) { - rawString = InternalJSONUtil.quote(value.toString()); - }else{ - rawString = formatDate(value, writer.getConfig().getDateFormat()); - } - writer.writeRaw(rawString); - } - - /** - * 按照给定格式格式化日期,格式为空时返回时间戳字符串
    - * 如果给定的格式是时间戳,直接返回时间戳字符串,如果是给定字符串格式,返回带双引号包装的字符串 - * - * @param dateObj Date或者Calendar对象 - * @param format 格式 - * @return 日期字符串 - */ - private static String formatDate(final Object dateObj, final String format) { - if (StrUtil.isNotBlank(format)) { - final String dateStr; - if (dateObj instanceof TemporalAccessor) { - dateStr = TemporalAccessorUtil.format((TemporalAccessor) dateObj, format); - } else { - dateStr = DateUtil.format(ConvertUtil.toDate(dateObj), format); - } - - if (GlobalCustomFormat.FORMAT_SECONDS.equals(format) - || GlobalCustomFormat.FORMAT_MILLISECONDS.equals(format)) { - // Hutool自定义的秒和毫秒表示,默认不包装双引号 - return dateStr; - } - //用户定义了日期格式 - return InternalJSONUtil.quote(dateStr); - } - - //默认使用时间戳 - final long timeMillis; - if (dateObj instanceof TemporalAccessor) { - timeMillis = TemporalAccessorUtil.toEpochMilli((TemporalAccessor) dateObj); - } else if (dateObj instanceof Date) { - timeMillis = ((Date) dateObj).getTime(); - } else if (dateObj instanceof Calendar) { - timeMillis = ((Calendar) dateObj).getTimeInMillis(); - } else { - throw new UnsupportedOperationException("Unsupported Date type: " + dateObj.getClass()); - } - return String.valueOf(timeMillis); - } -} diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/writer/impl/JdkValueWriter.java b/hutool-json/src/main/java/org/dromara/hutool/json/writer/impl/JdkValueWriter.java deleted file mode 100644 index ee270821d..000000000 --- a/hutool-json/src/main/java/org/dromara/hutool/json/writer/impl/JdkValueWriter.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * 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.writer.impl; - -import org.dromara.hutool.core.array.ArrayUtil; -import org.dromara.hutool.core.reflect.ClassUtil; -import org.dromara.hutool.json.writer.JSONWriter; -import org.dromara.hutool.json.writer.ValueWriter; - -import java.sql.SQLException; -import java.time.temporal.TemporalAccessor; -import java.util.Calendar; -import java.util.Date; -import java.util.Map; -import java.util.Optional; - -/** - * JDK内置类型的值写出器 - * - *

    - * 枚举类和JDK内部类直接使用toString输出,不做转换。 - *

    - * - *

    - * {@link SQLException}实现了Iterable导致被识别为列表,可能造成死循环,此处按照字符串处理,见: - * https://github.com/dromara/hutool/issues/1399 - *

    - * - * @author looly - * @since 6.0.0 - */ -public class JdkValueWriter implements ValueWriter { - /** - * 单例对象 - */ - public static final JdkValueWriter INSTANCE = new JdkValueWriter(); - - @Override - public boolean test(final Object value) { - if (null == value) { - return false; - } - if (value instanceof Enum || value instanceof SQLException) { - return true; - } - - // 自定义写出的跳过 - if (value instanceof Number - || value instanceof Date - || value instanceof Calendar - || value instanceof TemporalAccessor - || value instanceof Boolean - // 可转换为JSONObject和JSONArray的对象 - || value instanceof Map - || value instanceof Map.Entry - || value instanceof Iterable - || ArrayUtil.isArray(value) - || value instanceof Optional - ) { - return false; - } - - // Java内部类不做转换 - return ClassUtil.isJdkClass(value.getClass()); - } - - @Override - public void write(final JSONWriter writer, final Object value) { - final String valueStr; - if(value instanceof Class){ - valueStr = ((Class) value).getName(); - }else{ - valueStr = value.toString(); - } - writer.writeQuoteStrValue(valueStr); - } -} diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/writer/impl/NumberValueWriter.java b/hutool-json/src/main/java/org/dromara/hutool/json/writer/impl/NumberValueWriter.java deleted file mode 100644 index 2e28ac70c..000000000 --- a/hutool-json/src/main/java/org/dromara/hutool/json/writer/impl/NumberValueWriter.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * 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.writer.impl; - -import org.dromara.hutool.core.math.NumberUtil; -import org.dromara.hutool.json.JSONConfig; -import org.dromara.hutool.json.JSONException; -import org.dromara.hutool.json.writer.JSONWriter; -import org.dromara.hutool.json.writer.NumberWriteMode; -import org.dromara.hutool.json.writer.ValueWriter; - -/** - * 数字类型的值写出器 - * - * @author looly - * @since 6.0.0 - */ -public class NumberValueWriter implements ValueWriter { - - /** - * JS中表示的数字最大值 - */ - private static final long JS_MAX_NUMBER = 9007199254740992L; - - /** - * 单例对象 - */ - public static final NumberValueWriter INSTANCE = new NumberValueWriter(); - - @Override - public boolean test(final Object value) { - // 合法数字原样存储 - if(value instanceof Number){ - if(!NumberUtil.isValidNumber((Number) value)){ - throw new JSONException("JSON does not allow non-finite numbers."); - } - return true; - } - return false; - } - - /** - * 写出数字,根据{@link JSONConfig#isStripTrailingZeros()} 配置不同,写出不同数字
    - * 主要针对Double型是否去掉小数点后多余的0
    - * 此方法输出的值不包装引号。 - * - * @param writer {@link JSONWriter} - * @param value 数字 - */ - @Override - public void write(final JSONWriter writer, final Object value) { - final JSONConfig config = writer.getConfig(); - // since 5.6.2可配置是否去除末尾多余0,例如如果为true,5.0返回5 - final boolean isStripTrailingZeros = (null == config) || config.isStripTrailingZeros(); - final Number number = (Number) value; - final String numberStr = NumberUtil.toStr(number, isStripTrailingZeros); - - final NumberWriteMode numberWriteMode = (null == config) ? NumberWriteMode.NORMAL : config.getNumberWriteMode(); - switch (numberWriteMode){ - case JS: - if(number.longValue() > JS_MAX_NUMBER){ - writer.writeQuoteStrValue(numberStr); - } else{ - writer.writeRaw(numberStr); - } - break; - case STRING: - writer.writeQuoteStrValue(numberStr); - break; - default: - writer.writeRaw(numberStr); - break; - } - } -} diff --git a/hutool-json/src/test/java/org/dromara/hutool/json/CustomSerializeTest.java b/hutool-json/src/test/java/org/dromara/hutool/json/CustomSerializeTest.java index 72dd22856..9e4fc5933 100644 --- a/hutool-json/src/test/java/org/dromara/hutool/json/CustomSerializeTest.java +++ b/hutool-json/src/test/java/org/dromara/hutool/json/CustomSerializeTest.java @@ -19,7 +19,7 @@ package org.dromara.hutool.json; import lombok.ToString; import org.dromara.hutool.json.serializer.JSONDeserializer; import org.dromara.hutool.json.serializer.JSONSerializer; -import org.dromara.hutool.json.serializer.SerializerManager; +import org.dromara.hutool.json.serializer.TypeAdapterManager; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -30,7 +30,7 @@ public class CustomSerializeTest { @BeforeEach public void init() { - SerializerManager.getInstance().register(CustomBean.class, + TypeAdapterManager.getInstance().register(CustomBean.class, (JSONSerializer) (bean, context) -> ((JSONObject)context.getContextJson()).set("customName", bean.name)); } @@ -55,7 +55,7 @@ public class CustomSerializeTest { @Test public void deserializeTest() { - SerializerManager.getInstance().register(CustomBean.class, (JSONDeserializer) (json, deserializeType) -> { + TypeAdapterManager.getInstance().register(CustomBean.class, (JSONDeserializer) (json, deserializeType) -> { final CustomBean customBean = new CustomBean(); customBean.name = ((JSONObject) json).getStr("customName"); return customBean; diff --git a/hutool-json/src/test/java/org/dromara/hutool/json/Issue1399Test.java b/hutool-json/src/test/java/org/dromara/hutool/json/Issue1399Test.java index 444c53b03..4856fe2bd 100644 --- a/hutool-json/src/test/java/org/dromara/hutool/json/Issue1399Test.java +++ b/hutool-json/src/test/java/org/dromara/hutool/json/Issue1399Test.java @@ -16,6 +16,7 @@ package org.dromara.hutool.json; +import lombok.Data; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -23,12 +24,25 @@ import java.sql.SQLException; /** * https://github.com/dromara/hutool/issues/1399
    - * 异常SQLException实现了Iterable导致被识别为列表,可能造成死循环,此处按照字符串处理 + * Throwable的默认序列化策略 */ public class Issue1399Test { @Test void sqlExceptionTest() { final JSONObject set = JSONUtil.ofObj().set("error", new SQLException("test")); - Assertions.assertEquals("{\"error\":\"java.sql.SQLException: test\"}", set.toString()); + + final String jsonStr = set.toString(); + Assertions.assertEquals("{\"error\":\"java.sql.SQLException: test\"}", jsonStr); + + final ErrorBean bean = set.toBean(ErrorBean.class); + Assertions.assertNotNull(bean); + Assertions.assertNotNull(bean.getError()); + Assertions.assertEquals(SQLException.class, bean.getError().getClass()); + Assertions.assertEquals("test", bean.getError().getMessage()); + } + + @Data + private static class ErrorBean { + private SQLException error; } } diff --git a/hutool-json/src/test/java/org/dromara/hutool/json/Issue2555Test.java b/hutool-json/src/test/java/org/dromara/hutool/json/Issue2555Test.java index fa1cf077f..3cba24eb4 100644 --- a/hutool-json/src/test/java/org/dromara/hutool/json/Issue2555Test.java +++ b/hutool-json/src/test/java/org/dromara/hutool/json/Issue2555Test.java @@ -20,7 +20,7 @@ import lombok.Data; import org.dromara.hutool.json.serializer.JSONContext; import org.dromara.hutool.json.serializer.JSONDeserializer; import org.dromara.hutool.json.serializer.JSONSerializer; -import org.dromara.hutool.json.serializer.SerializerManager; +import org.dromara.hutool.json.serializer.TypeAdapterManager; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -29,8 +29,8 @@ import java.lang.reflect.Type; public class Issue2555Test { @Test public void serAndDeserTest(){ - SerializerManager.getInstance().register(MyType.class, new MySerializer()); - SerializerManager.getInstance().register(MyType.class, new MyDeserializer()); + TypeAdapterManager.getInstance().register(MyType.class, new MySerializer()); + TypeAdapterManager.getInstance().register(MyType.class, new MyDeserializer()); final SimpleObj simpleObj = new SimpleObj(); final MyType child = new MyType(); diff --git a/hutool-json/src/test/java/org/dromara/hutool/json/Issue3086Test.java b/hutool-json/src/test/java/org/dromara/hutool/json/Issue3086Test.java index 160736f1f..528e5251f 100644 --- a/hutool-json/src/test/java/org/dromara/hutool/json/Issue3086Test.java +++ b/hutool-json/src/test/java/org/dromara/hutool/json/Issue3086Test.java @@ -20,7 +20,7 @@ import lombok.Data; import org.dromara.hutool.core.collection.ListUtil; import org.dromara.hutool.json.serializer.JSONContext; import org.dromara.hutool.json.serializer.JSONSerializer; -import org.dromara.hutool.json.serializer.SerializerManager; +import org.dromara.hutool.json.serializer.TypeAdapterManager; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -33,7 +33,7 @@ public class Issue3086Test { @Test public void serializeTest() { - SerializerManager.getInstance().register(TestBean.class, new TestBean()); + TypeAdapterManager.getInstance().register(TestBean.class, new TestBean()); final List strings = ListUtil.of( new SimpleGrantedAuthority("ROLE_admin"), diff --git a/hutool-json/src/test/java/org/dromara/hutool/json/Issue3504Test.java b/hutool-json/src/test/java/org/dromara/hutool/json/Issue3504Test.java index f1450bc5c..ccda7f965 100644 --- a/hutool-json/src/test/java/org/dromara/hutool/json/Issue3504Test.java +++ b/hutool-json/src/test/java/org/dromara/hutool/json/Issue3504Test.java @@ -17,19 +17,30 @@ package org.dromara.hutool.json; import lombok.Data; -import org.dromara.hutool.core.lang.Console; +import org.dromara.hutool.core.reflect.ClassUtil; +import org.dromara.hutool.core.util.ObjUtil; +import org.dromara.hutool.json.serializer.JSONContext; +import org.dromara.hutool.json.serializer.JSONDeserializer; +import org.dromara.hutool.json.serializer.JSONSerializer; +import org.dromara.hutool.json.serializer.TypeAdapterManager; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class Issue3504Test { @Test public void test3504() { + // 考虑到安全性,Class默认不支持序列化和反序列化,需要自行注册 + TypeAdapterManager.getInstance().register(Class.class, + (JSONSerializer>) (bean, context) -> new JSONPrimitive(bean.getName(), ObjUtil.apply(context, JSONContext::config))); + TypeAdapterManager.getInstance().register(Class.class, + (JSONDeserializer>) (json, deserializeType) -> ClassUtil.forName((String)json.asJSONPrimitive().getValue(), true, null)); + final JsonBean jsonBean = new JsonBean(); jsonBean.setName("test"); jsonBean.setClasses(new Class[]{String.class}); - final String huToolJsonStr = JSONUtil.toJsonStr(jsonBean); - Console.log(huToolJsonStr); - final JsonBean bean = JSONUtil.toBean(huToolJsonStr, JsonBean.class); + + final String jsonStr = JSONUtil.toJsonStr(jsonBean); + final JsonBean bean = JSONUtil.toBean(jsonStr, JsonBean.class); Assertions.assertNotNull(bean); Assertions.assertEquals("test", bean.getName()); } diff --git a/hutool-json/src/test/java/org/dromara/hutool/json/Issue867Test.java b/hutool-json/src/test/java/org/dromara/hutool/json/Issue867Test.java index 5f2824d9a..af7972f76 100644 --- a/hutool-json/src/test/java/org/dromara/hutool/json/Issue867Test.java +++ b/hutool-json/src/test/java/org/dromara/hutool/json/Issue867Test.java @@ -16,8 +16,8 @@ package org.dromara.hutool.json; -import org.dromara.hutool.core.annotation.Alias; import lombok.Data; +import org.dromara.hutool.core.annotation.Alias; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -25,8 +25,10 @@ public class Issue867Test { @Test public void toBeanTest(){ - final String json = "{\"abc_1d\":\"123\",\"abc_d\":\"456\",\"abc_de\":\"789\"}"; - final Test02 bean = JSONUtil.toBean(JSONUtil.parseObj(json),Test02.class); + final String jsonStr = "{\"abc_1d\":\"123\",\"abc_d\":\"456\",\"abc_de\":\"789\"}"; + final JSONObject json = JSONUtil.parseObj(jsonStr); + //Console.log(json); + final Test02 bean = JSONUtil.toBean(json,Test02.class); Assertions.assertEquals("123", bean.getAbc1d()); Assertions.assertEquals("456", bean.getAbcD()); Assertions.assertEquals("789", bean.getAbcDe()); diff --git a/hutool-json/src/test/java/org/dromara/hutool/json/IssuesI44E4HTest.java b/hutool-json/src/test/java/org/dromara/hutool/json/IssuesI44E4HTest.java index 2fd49c7d9..1c1fe4b63 100644 --- a/hutool-json/src/test/java/org/dromara/hutool/json/IssuesI44E4HTest.java +++ b/hutool-json/src/test/java/org/dromara/hutool/json/IssuesI44E4HTest.java @@ -20,7 +20,7 @@ import lombok.AllArgsConstructor; import lombok.Getter; import lombok.Setter; import org.dromara.hutool.json.serializer.JSONDeserializer; -import org.dromara.hutool.json.serializer.SerializerManager; +import org.dromara.hutool.json.serializer.TypeAdapterManager; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -31,7 +31,7 @@ public class IssuesI44E4HTest { @Test public void deserializerTest(){ - SerializerManager.getInstance().register(TestDto.class, (JSONDeserializer) (json, deserializeType) -> { + TypeAdapterManager.getInstance().register(TestDto.class, (JSONDeserializer) (json, deserializeType) -> { final TestDto testDto = new TestDto(); testDto.setMd(new AcBizModuleMd("name1", ((JSONObject)json).getStr("md"))); return testDto; diff --git a/hutool-json/src/test/java/org/dromara/hutool/json/writer/ValueWriterManagerTest.java b/hutool-json/src/test/java/org/dromara/hutool/json/writer/ValueWriterManagerTest.java deleted file mode 100644 index ea15494b1..000000000 --- a/hutool-json/src/test/java/org/dromara/hutool/json/writer/ValueWriterManagerTest.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * 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.writer; - -import lombok.Data; -import org.dromara.hutool.core.convert.Converter; -import org.dromara.hutool.json.JSONConfig; -import org.dromara.hutool.json.JSONUtil; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -public class ValueWriterManagerTest { - - @BeforeEach - public void init(){ - ValueWriterManager.getInstance().register(new ValueWriter() { - @Override - public void write(final JSONWriter writer, final Object value) { - writer.writeRaw(String.valueOf(((CustomSubBean)value).getId())); - } - - @Override - public boolean test(final Object value) { - return value instanceof CustomSubBean; - } - }); - } - - @Test - public void customWriteTest(){ - final CustomSubBean customBean = new CustomSubBean(); - customBean.setId(12); - customBean.setName("aaa"); - final String s = JSONUtil.toJsonStr(customBean); - Assertions.assertEquals("12", s); - } - - @Test - public void customWriteSubTest(){ - final CustomSubBean customSubBean = new CustomSubBean(); - customSubBean.setId(12); - customSubBean.setName("aaa"); - final CustomBean customBean = new CustomBean(); - customBean.setId(1); - customBean.setSub(customSubBean); - - final String s = JSONUtil.toJsonStr(customBean); - Assertions.assertEquals("{\"id\":1,\"sub\":12}", s); - - // 自定义转换 - final JSONConfig jsonConfig = JSONConfig.of(); - final Converter converter = jsonConfig.getConverter(); - jsonConfig.setConverter((targetType, value) -> { - if(targetType == CustomSubBean.class){ - final CustomSubBean subBean = new CustomSubBean(); - subBean.setId((Integer) value); - return subBean; - } - return converter.convert(targetType, value); - }); - final CustomBean customBean1 = JSONUtil.parseObj(s, jsonConfig).toBean(CustomBean.class); - Assertions.assertEquals(12, customBean1.getSub().getId()); - } - - @Data - static class CustomSubBean { - private int id; - private String name; - } - - @Data - static class CustomBean{ - private int id; - private CustomSubBean sub; - } -}