This commit is contained in:
Looly 2024-09-22 10:16:56 +08:00
parent f770e00904
commit 2fdbb46259
46 changed files with 721 additions and 796 deletions

View File

@ -62,19 +62,19 @@ public class ValueProviderToBeanCopier<T> extends AbsCopier<ValueProvider<String
}
final Map<String, PropDesc> 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<Object, Object> entry = copyOptions.editField(tFieldName, null);
if(null == entry){
@ -92,12 +92,12 @@ public class ValueProviderToBeanCopier<T> extends AbsCopier<ValueProvider<String
final Object sValue = source.value(tFieldName, fieldType);
// 检查目标对象属性是否过滤属性
if (!copyOptions.testPropertyFilter(tDesc.getField(), sValue)) {
if (!copyOptions.testPropertyFilter(propDesc.getField(), sValue)) {
return;
}
// 目标赋值
tDesc.setValue(this.target, sValue, copyOptions.ignoreNullValue, copyOptions.ignoreError, copyOptions.override);
propDesc.setValue(this.target, sValue, copyOptions.ignoreNullValue, copyOptions.ignoreError, copyOptions.override);
});
return this.target;
}

View File

@ -29,14 +29,31 @@ import java.util.Date;
* 日期转换器
*
* @author Looly
*
*/
public class CalendarConverter extends AbstractConverter {
private static final long serialVersionUID = 1L;
/** 日期格式化 */
/**
* 日期格式化
*/
private String format;
/**
* 构造
*/
public CalendarConverter() {
this(null);
}
/**
* 构造
*
* @param format 日期格式{@code null}表示无格式定义
*/
public CalendarConverter(final String format) {
this.format = format;
}
/**
* 获取日期格式
*
@ -59,16 +76,16 @@ public class CalendarConverter extends AbstractConverter {
protected Calendar convertInternal(final Class<?> 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);
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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();
}
/**

View File

@ -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<Map<String, String>>() {});
Assertions.assertEquals(Map.class, aClass);
}
@Test
public void getEleTypeTest() {
final Method method = MethodUtil.getMethod(TestClass.class, "getList");

View File

@ -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));
}
/**

View File

@ -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中嵌套的对象<br>
* <ol>
@ -141,7 +167,13 @@ public interface JSON extends Converter, Serializable {
*/
@SuppressWarnings("unchecked")
default <T> 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<Object> 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> 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<Object> deserializer = TypeAdapterManager.getInstance().getDeserializer(this, type);
if(null == deserializer){
throw new JSONException("No deserializer for type: " + type);
}
return (T) deserializer.deserialize(this, type);
}
}

View File

@ -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<JSON> implements JSON, JSONGetter<Int
* 配置项
*/
private final JSONConfig config;
private final JSONValueMapper mapper;
// region ----- Constructors
@ -95,6 +97,7 @@ public class JSONArray extends ListWrapper<JSON> implements JSON, JSONGetter<Int
public JSONArray(final int initialCapacity, final JSONConfig config) {
super(new ArrayList<>(initialCapacity));
this.config = ObjUtil.defaultIfNull(config, JSONConfig::of);
this.mapper = JSONValueMapper.of(config, null);
}
// endregion
@ -129,7 +132,7 @@ public class JSONArray extends ListWrapper<JSON> implements JSON, JSONGetter<Int
* @since 5.2.5
*/
public JSONArray set(final Object value) {
this.add(config.getConverter().convert(JSON.class, value));
this.add(mapper.map(value));
return this;
}
@ -213,7 +216,7 @@ public class JSONArray extends ListWrapper<JSON> implements JSON, JSONGetter<Int
* @return 替换的值即之前的值
*/
public JSON setValue(final int index, final Object element) {
return set(index, config.getConverter().convert(JSON.class, element));
return set(index, mapper.map(element));
}
/**

View File

@ -17,8 +17,6 @@
package org.dromara.hutool.json;
import org.dromara.hutool.core.comparator.CompareUtil;
import org.dromara.hutool.core.convert.Converter;
import org.dromara.hutool.json.convert.JSONConverter;
import org.dromara.hutool.json.writer.NumberWriteMode;
import java.io.Serializable;
@ -70,10 +68,6 @@ public class JSONConfig implements Serializable {
* 是否检查重复key
*/
private boolean checkDuplicate;
/**
* 自定义的类型转换器用于在getXXX操作中自动转换类型
*/
private Converter converter = JSONConverter.of(this);
/**
* Number写出模式
*/
@ -274,24 +268,6 @@ public class JSONConfig implements Serializable {
return this;
}
/**
* 获取自定义的类型转换器用于在序列化反序列化操作中实现对象类型转换
*
* @return 转换器
*/
public Converter getConverter() {
return converter;
}
/**
* 设置自定义的类型转换器用于在序列化反序列化操作中实现对象类型转换
*
* @param converter 转换器
*/
public void setConverter(final Converter converter) {
this.converter = converter;
}
/**
* 获取Number写出模式
*

View File

@ -16,8 +16,11 @@
package org.dromara.hutool.json;
import org.dromara.hutool.core.convert.CompositeConverter;
import org.dromara.hutool.core.lang.getter.TypeGetter;
import org.dromara.hutool.core.util.ObjUtil;
import org.dromara.hutool.json.serializer.JSONDeserializer;
import org.dromara.hutool.json.serializer.TypeAdapterManager;
import java.lang.reflect.Type;
import java.util.List;
@ -144,13 +147,24 @@ public interface JSONGetter<K> extends TypeGetter<K> {
return (null == jsonArray) ? null : jsonArray.toList(beanType);
}
@SuppressWarnings("unchecked")
@Override
default <T> 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<Object> 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);
}
}

View File

@ -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<String, JSON> implements JSON, JSONGe
* 配置项
*/
private final JSONConfig config;
private final JSONValueMapper mapper;
/**
* 构造初始容量为 {@link #DEFAULT_CAPACITY}KEY有序
@ -79,6 +81,7 @@ public class JSONObject extends MapWrapper<String, JSON> 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<String, JSON> 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;
}

View File

@ -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<Object>, 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<Object>, JSON {
return 1;
}
@Override
public <T> 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<Object>, JSON {
write(jsonWriter);
return jsonWriter.toString();
}
/**
* 写出数字根据{@link JSONConfig#isStripTrailingZeros()} 配置不同写出不同数字<br>
* 主要针对Double型是否去掉小数点后多余的0<br>
* 此方法输出的值不包装引号
*
* @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;
}
}
}

View File

@ -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;
}

View File

@ -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> 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);
}

View File

@ -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<MutableEntry<Object, Object>> 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<Object> serializer = SerializerManager.getInstance().getSerializer(obj);
final JSONSerializer<Object> 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);
}
}

View File

@ -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);
}
}

View File

@ -31,7 +31,7 @@ import java.lang.reflect.Type;
* @author Looly
*/
@FunctionalInterface
public interface JSONDeserializer<V> {
public interface JSONDeserializer<V> extends TypeAdapter{
/**
* 反序列化通过实现此方法自定义实现JSON转换为指定类型的逻辑<br>

View File

@ -27,7 +27,7 @@ import org.dromara.hutool.json.JSONObject;
* @author Looly
*/
@FunctionalInterface
public interface JSONSerializer<V> {
public interface JSONSerializer<V> extends TypeAdapter{
/**
* 序列化实现通过实现此方法将指定类型的对象转换为{@link JSON}对象,可以

View File

@ -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{}

View File

@ -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序列化器注册和注销自定义序列化器和反序列化器<br>
* 此管理器管理着两种类型的序列化器和反序列化器
* <ul>
* <li>类型精准匹配方式通过Java对象类型匹配只会匹配查找的类型而不匹配子类可以调用{@link #register(Type, JSONSerializer)} {@link #register(Type, JSONDeserializer)}注册</li>
* <li>匹配器Matcher方式通过判断序列化和反序列化器中match方法找到自定义的序列化和反序列化器可以调用{@link #register(MatcherJSONSerializer)} {@link #register(MatcherJSONDeserializer)}注册</li>
* <li>类型精准匹配方式通过Java对象类型匹配只会匹配查找的类型而不匹配子类可以调用{@link #register(Type, TypeAdapter)}注册</li>
* <li>匹配器Matcher方式通过判断序列化和反序列化器中match方法找到自定义的序列化和反序列化器可以调用{@link #register(TypeAdapter)}注册</li>
* </ul>
*
* <p>
* 管理器的使用分为三种方式
* <ul>
* <li>全局模式 使用{@link SerializerManager#getInstance()}调用单例全局可用</li>
* <li>实例模式 使用{@link SerializerManager#of()}创建实例局部可用</li>
* <li>全局模式 使用{@link TypeAdapterManager#getInstance()}调用单例全局可用</li>
* <li>实例模式 使用{@link TypeAdapterManager#of()}创建实例局部可用</li>
* <li>自定义模式使用{@code new SerializerManager()}创建实例不加载默认的转换器</li>
* </ul>
*
* @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
/**
* 注册自定义序列化器用于自定义对象序列化<br>
* 当按照匹配规则匹配时使用对应的序列化器进行序列化
* 注册自定义类型适配器用于自定义对象序列化和反序列化<br>
* 提供的适配器必须为实现{@link MatcherJSONSerializer}{@link MatcherJSONDeserializer}接口<br>
* 当两个接口都实现时同时注册序列化和反序列化器
*
* @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());
}
/**
* 注册自定义序列化器用于自定义对象序列化<br>
* 当类型精准匹配时使用对应的序列化器进行序列化
* 注册自定义类型适配器用于自定义对象序列化和反序列化
*
* @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;
}
/**
* 注册自定义反序列化器用于自定义对象反序列化<br>
* 当按照匹配规则匹配时使用对应的反序列化器进行反序列化
*
* @param deserializer 自定义反序列化器
* @return this
*/
public SerializerManager register(final MatcherJSONDeserializer<?> deserializer) {
if (null != deserializer) {
getDeserializerSet().add(deserializer);
}
return this;
}
/**
* 注册自定义反序列化器用于自定义对象反序列化<br>
* 当类型精准匹配时使用对应的反序列化器进行反序列化
*
* @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<Object> getSerializer(final Object bean) {
if(CollUtil.isNotEmpty(this.serializerSet)){
for (final MatcherJSONSerializer<?> serializer : this.serializerSet) {
if (serializer.match(bean, null)) {
return (MatcherJSONSerializer<Object>) serializer;
}
}
}
return null;
}
/**
* 获取匹配器对应的序列化器
*
* @param type 类型
* @return JSONSerializer
*/
@SuppressWarnings("unchecked")
public JSONSerializer<Object> getSerializer(final Type type) {
if(null == type || CollUtil.isEmpty(this.serializerMap)){
return null;
@SuppressWarnings({"unchecked"})
public JSONSerializer<Object> getSerializer(final Object bean, final Type type) {
JSONSerializer<Object> result = null;
if (null != type && MapUtil.isNotEmpty(this.serializerMap)) {
result = (JSONSerializer<Object>) this.serializerMap.get(type);
}
return (JSONSerializer<Object>) this.serializerMap.get(type);
if (null == result && CollUtil.isNotEmpty(this.serializerSet)) {
for (final MatcherJSONSerializer<?> serializer : this.serializerSet) {
if (serializer.match(bean, null)) {
result = (MatcherJSONSerializer<Object>) 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<Object> 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<Object>) 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<Object>) 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<Object>) 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<TimeZone>) TimeZoneSerializer.INSTANCE);
manager.register((MatcherJSONDeserializer<TimeZone>) 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
}

View File

@ -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<Object>, MatcherJSONDeserializer<Object> {
/**
* 单例
*/
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<Object> copier = new ValueProviderToBeanCopier<>(
new JSONObjectValueProvider((JSONObject) json),
ConstructorUtil.newInstanceIfPossible(TypeUtil.getClass(deserializeType)),
deserializeType,
InternalJSONUtil.toCopyOptions(json.config())
);
return copier.copy();
}
}

View File

@ -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<Calendar>, MatcherJSONDeserializer<Calendar> {
/**
* 单例
*/
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());
}
}

View File

@ -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<Date>, MatcherJSONDeserializer<Date> {
/**
* 单例
*/
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());
}
}

View File

@ -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<Object> {
/**
* 单例
*/
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<Object> {
* @return 反序列化后的对象
*/
private Object fromJSONObject(final JSONObject json, final Type deserializeType, final Class<?> rawType) {
// 转为POJO
final Copier<Object> 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<Object> {
* @return 反序列化后的对象
*/
private Object fromJSONArray(final JSONArray json, final Type deserializeType, final Class<?> rawType) {
return json;
throw new JSONException("Unsupported JSONArray to {}", rawType);
}
/**

View File

@ -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<Object>, MatcherJSONDeserializer<Object> {
/**
* 单例
*/
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());
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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;
* </ul>
*
* @author looly
* @since 5.7.22
*/
public class TemporalAccessorSerializer implements JSONSerializer<TemporalAccessor>, JSONDeserializer<TemporalAccessor> {
public class TemporalTypeAdapter implements MatcherJSONSerializer<TemporalAccessor>, MatcherJSONDeserializer<TemporalAccessor> {
private static final String YEAR_KEY = "year";
private static final String MONTH_KEY = "month";
@ -53,15 +53,19 @@ public class TemporalAccessorSerializer implements JSONSerializer<TemporalAccess
private static final String SECOND_KEY = "second";
private static final String NANO_KEY = "nano";
private final Class<? extends TemporalAccessor> temporalAccessorClass;
/**
* 构造
*
* @param temporalAccessorClass TemporalAccessor实现类型
* 单例
*/
public TemporalAccessorSerializer(final Class<? extends TemporalAccessor> 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<TemporalAccess
// TODO JSONArray
final Class<?> 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<TemporalAccess
}
final LocalDate localDate = LocalDate.of(year, month, day);
if (LocalDate.class.equals(this.temporalAccessorClass)) {
if (LocalDate.class.equals(temporalAccessorClass)) {
return localDate;
}
@ -147,7 +152,7 @@ public class TemporalAccessorSerializer implements JSONSerializer<TemporalAccess
jsonObject.getInt(NANO_KEY, 0));
return LocalDateTime.of(localDate, localTime);
} else if (LocalTime.class.equals(this.temporalAccessorClass)) {
} else if (LocalTime.class.equals(temporalAccessorClass)) {
return LocalTime.of(
jsonObject.getInt(HOUR_KEY),
jsonObject.getInt(MINUTE_KEY),
@ -155,6 +160,6 @@ public class TemporalAccessorSerializer implements JSONSerializer<TemporalAccess
jsonObject.getInt(NANO_KEY));
}
throw new JSONException("Unsupported type from JSON: {}", this.temporalAccessorClass);
throw new JSONException("Unsupported type from JSON: {}", deserializeType);
}
}

View File

@ -0,0 +1,67 @@
/*
* 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.ConstructorUtil;
import org.dromara.hutool.core.reflect.TypeUtil;
import org.dromara.hutool.core.text.StrUtil;
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;
/**
* Throwable类型适配器用于将Throwable对象转换为JSON对象
*
* @author looly
* @since 6.0.0
*/
public class ThrowableTypeAdapter implements MatcherJSONSerializer<Throwable>, MatcherJSONDeserializer<Throwable> {
/**
* 单例
*/
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));
}
}

View File

@ -27,17 +27,17 @@ import java.lang.reflect.Type;
import java.util.TimeZone;
/**
* 时区序列化
* 时区类型适配
*
* @author looly
* @since 6.0.0
*/
public class TimeZoneSerializer implements MatcherJSONSerializer<TimeZone>, MatcherJSONDeserializer<TimeZone> {
public class TimeZoneTypeAdapter implements MatcherJSONSerializer<TimeZone>, MatcherJSONDeserializer<TimeZone> {
/**
* 单例
*/
public static final TimeZoneSerializer INSTANCE = new TimeZoneSerializer();
public static final TimeZoneTypeAdapter INSTANCE = new TimeZoneTypeAdapter();
@Override
public boolean match(final JSON json, final Type deserializeType) {

View File

@ -27,5 +27,16 @@
* <li>序列化Serialize Java对象 转换为 JSON对象</li>
* <li>反序列化DeserializeJSON对象 转换为 Java对象</li>
* </ul>
*
* 3. JSON序列化实现
* <ul>
* <li>TypeAdapter类型适配器标记序列化或反序列化</li>
* <li>JSONSerializerJSON序列化接口用于自定义序列化</li>
* <li>JSONDeserializerJSON反序列化接口用于自定义反序列化</li>
* </ul>
*
* 4. JSON序列化管理<br>
* TypeAdapterManager用于管理定义的序列化和反序列化器
*
*/
package org.dromara.hutool.json.serializer;

View File

@ -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);

View File

@ -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<ValueWriter> 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<ValueWriter> 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);
}
}

View File

@ -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());
}
}

View File

@ -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;
/**
* 日期类型的值写出器<br>
* 支持包括{@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);
}
/**
* 按照给定格式格式化日期格式为空时返回时间戳字符串<br>
* 如果给定的格式是时间戳直接返回时间戳字符串如果是给定字符串格式返回带双引号包装的字符串
*
* @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);
}
}

View File

@ -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内置类型的值写出器
*
* <p>
* 枚举类和JDK内部类直接使用toString输出不做转换
* </p>
*
* <p>
* {@link SQLException}实现了Iterable导致被识别为列表可能造成死循环此处按照字符串处理
* https://github.com/dromara/hutool/issues/1399
* </p>
*
* @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);
}
}

View File

@ -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()} 配置不同写出不同数字<br>
* 主要针对Double型是否去掉小数点后多余的0<br>
* 此方法输出的值不包装引号
*
* @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;
}
}
}

View File

@ -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<CustomBean>) (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<CustomBean>) (json, deserializeType) -> {
TypeAdapterManager.getInstance().register(CustomBean.class, (JSONDeserializer<CustomBean>) (json, deserializeType) -> {
final CustomBean customBean = new CustomBean();
customBean.name = ((JSONObject) json).getStr("customName");
return customBean;

View File

@ -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<br>
* 异常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;
}
}

View File

@ -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();

View File

@ -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<SimpleGrantedAuthority> strings = ListUtil.of(
new SimpleGrantedAuthority("ROLE_admin"),

View File

@ -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<Class<?>>) (bean, context) -> new JSONPrimitive(bean.getName(), ObjUtil.apply(context, JSONContext::config)));
TypeAdapterManager.getInstance().register(Class.class,
(JSONDeserializer<Class<?>>) (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());
}

View File

@ -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());

View File

@ -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<TestDto>) (json, deserializeType) -> {
TypeAdapterManager.getInstance().register(TestDto.class, (JSONDeserializer<TestDto>) (json, deserializeType) -> {
final TestDto testDto = new TestDto();
testDto.setMd(new AcBizModuleMd("name1", ((JSONObject)json).getStr("md")));
return testDto;

View File

@ -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;
}
}