From 3ece54bf5826a13aac5889ae26d3fad506704b0b Mon Sep 17 00:00:00 2001 From: Looly Date: Sun, 9 Oct 2022 01:12:02 +0800 Subject: [PATCH] fix code --- .../java/cn/hutool/core/reflect/NullType.java | 23 +++++ .../java/cn/hutool/json/InternalJSONUtil.java | 58 +++++++++-- .../main/java/cn/hutool/json/JSONConfig.java | 38 +------ .../main/java/cn/hutool/json/JSONParser.java | 7 +- .../main/java/cn/hutool/json/JSONTokener.java | 53 +++------- .../main/java/cn/hutool/json/JSONUtil.java | 18 +++- .../cn/hutool/json/convert/JSONConverter.java | 98 ++++++++++++------- .../convert/JSONDeserializerConverter.java | 36 ------- .../serialize/GlobalSerializeMapping.java | 34 ++++--- .../hutool/json/serialize/package-info.java | 4 + .../json/writer/GlobalValueWriterMapping.java | 44 +++++++++ .../hutool/json/writer/JSONValueWriter.java | 3 + .../cn/hutool/json/writer/JSONWriter.java | 14 ++- .../writer/GlobalValueWriterMappingTest.java | 67 +++++++++++++ 14 files changed, 312 insertions(+), 185 deletions(-) create mode 100644 hutool-core/src/main/java/cn/hutool/core/reflect/NullType.java delete mode 100644 hutool-json/src/main/java/cn/hutool/json/convert/JSONDeserializerConverter.java create mode 100644 hutool-json/src/main/java/cn/hutool/json/serialize/package-info.java create mode 100644 hutool-json/src/main/java/cn/hutool/json/writer/GlobalValueWriterMapping.java create mode 100644 hutool-json/src/test/java/cn/hutool/json/writer/GlobalValueWriterMappingTest.java diff --git a/hutool-core/src/main/java/cn/hutool/core/reflect/NullType.java b/hutool-core/src/main/java/cn/hutool/core/reflect/NullType.java new file mode 100644 index 000000000..5bf5790a5 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/reflect/NullType.java @@ -0,0 +1,23 @@ +package cn.hutool.core.reflect; + +import java.lang.reflect.Type; + +/** + * 空类型表示 + * + * @author looly + * @since 6.0.0 + */ +public class NullType implements Type { + /** + * 单例对象 + */ + public static NullType INSTANCE = new NullType(); + + private NullType(){} + + @Override + public String toString() { + return "Type of null"; + } +} diff --git a/hutool-json/src/main/java/cn/hutool/json/InternalJSONUtil.java b/hutool-json/src/main/java/cn/hutool/json/InternalJSONUtil.java index c2e8724e7..a8931d3ad 100755 --- a/hutool-json/src/main/java/cn/hutool/json/InternalJSONUtil.java +++ b/hutool-json/src/main/java/cn/hutool/json/InternalJSONUtil.java @@ -9,25 +9,26 @@ import cn.hutool.core.map.CaseInsensitiveLinkedMap; import cn.hutool.core.map.CaseInsensitiveTreeMap; import cn.hutool.core.math.NumberUtil; import cn.hutool.core.reflect.ClassUtil; +import cn.hutool.core.reflect.ConstructorUtil; +import cn.hutool.core.reflect.TypeUtil; import cn.hutool.core.text.StrUtil; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.CharUtil; import cn.hutool.core.util.ObjUtil; +import cn.hutool.json.serialize.GlobalSerializeMapping; +import cn.hutool.json.serialize.JSONDeserializer; import cn.hutool.json.serialize.JSONString; +import cn.hutool.json.writer.GlobalValueWriterMapping; +import cn.hutool.json.writer.JSONValueWriter; import java.io.IOException; import java.io.StringWriter; import java.io.Writer; +import java.lang.reflect.Type; import java.math.BigDecimal; import java.sql.SQLException; import java.time.temporal.TemporalAccessor; -import java.util.Calendar; -import java.util.Collection; -import java.util.Comparator; -import java.util.Date; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.TreeMap; +import java.util.*; import java.util.function.Predicate; /** @@ -56,9 +57,11 @@ public final class InternalJSONUtil { * @return 包装后的值,null表示此值需被忽略 */ static Object wrap(final Object object, final JSONConfig jsonConfig) { - if (object == null) { - return null; + // null和自定义对象原样存储 + if (null == object || null != InternalJSONUtil.getValueWriter(object)) { + return object; } + if (object instanceof JSON // || object instanceof JSONString // || object instanceof CharSequence // @@ -369,7 +372,44 @@ public final class InternalJSONUtil { return rawHashMap; } + /** + * 根据值类型获取{@link JSONValueWriter},首先判断对象是否实现了{@link JSONValueWriter}接口
+ * 如果未实现从{@link GlobalValueWriterMapping}中查找全局的writer,否则返回null。 + * + * @param value 值 + * @param 值类型 + * @return {@link JSONValueWriter} + */ + @SuppressWarnings("unchecked") + public static JSONValueWriter getValueWriter(final T value) { + if (value instanceof JSONValueWriter) { + return (JSONValueWriter) value; + } + // 全局自定义序列化,支持null的自定义写出 + return (JSONValueWriter) GlobalValueWriterMapping.get(null == value ? null : value.getClass()); + } + + /** + * 根据目标类型,获取对应的{@link JSONDeserializer},首先判断是否实现了{@link JSONDeserializer}接口
+ * 如果未实现从{@link GlobalSerializeMapping}中查找全局的{@link JSONDeserializer},否则返回null + * + * @param targetType 目标类型 + * @param 目标类型 + * @return {@link JSONDeserializer} + */ + @SuppressWarnings("unchecked") + public static JSONDeserializer getDeserializer(final Type targetType) { + final Class rawType = (Class) TypeUtil.getClass(targetType); + if (null != rawType && JSONDeserializer.class.isAssignableFrom(rawType)) { + return (JSONDeserializer) ConstructorUtil.newInstanceIfPossible(rawType); + } + + // 全局自定义反序列化(优先级低于实现JSONDeserializer接口) + return (JSONDeserializer) GlobalSerializeMapping.getDeserializer(targetType); + } + // --------------------------------------------------------------------------------------------- Private method start + /** * 对所有双引号做转义处理(使用双反斜杠做转义)
* 为了能在HTML中较好的显示,会将</转义为<\/
diff --git a/hutool-json/src/main/java/cn/hutool/json/JSONConfig.java b/hutool-json/src/main/java/cn/hutool/json/JSONConfig.java index bfcffeb5f..c550c7b37 100755 --- a/hutool-json/src/main/java/cn/hutool/json/JSONConfig.java +++ b/hutool-json/src/main/java/cn/hutool/json/JSONConfig.java @@ -1,19 +1,11 @@ package cn.hutool.json; import cn.hutool.core.comparator.CompareUtil; -import cn.hutool.core.convert.Convert; import cn.hutool.core.convert.Converter; -import cn.hutool.core.convert.impl.DateConverter; -import cn.hutool.core.convert.impl.TemporalAccessorConverter; -import cn.hutool.core.reflect.TypeUtil; -import cn.hutool.core.text.StrUtil; import cn.hutool.json.convert.JSONConverter; -import cn.hutool.json.serialize.JSONString; import java.io.Serializable; -import java.time.temporal.TemporalAccessor; import java.util.Comparator; -import java.util.Date; /** * JSON配置项 @@ -59,35 +51,7 @@ public class JSONConfig implements Serializable { /** * 自定义的类型转换器,用于在getXXX操作中自动转换类型 */ - private Converter converter = (type, value)->{ - if(null == value){ - return null; - } - if(value instanceof JSONString){ - // 被JSONString包装的对象,获取其原始类型 - value = ((JSONString) value).getRaw(); - } - - final Class rawType = TypeUtil.getClass(type); - if(null == rawType){ - return value; - } - if(JSON.class.isAssignableFrom(rawType)){ - return JSONConverter.INSTANCE.toJSON(value); - } - if(Date.class.isAssignableFrom(rawType) || TemporalAccessor.class.isAssignableFrom(rawType)){ - // 日期转换,支持自定义日期格式 - final String format = getDateFormat(); - if (StrUtil.isNotBlank(format)) { - if (Date.class.isAssignableFrom(rawType)) { - return new DateConverter(format).convert(type, value); - } else { - return new TemporalAccessorConverter(format).convert(type, value); - } - } - } - return Convert.convertWithCheck(type, value, null, isIgnoreError()); - }; + private Converter converter = JSONConverter.of(this); /** * 创建默认的配置项 diff --git a/hutool-json/src/main/java/cn/hutool/json/JSONParser.java b/hutool-json/src/main/java/cn/hutool/json/JSONParser.java index a82d126a1..ab5423817 100755 --- a/hutool-json/src/main/java/cn/hutool/json/JSONParser.java +++ b/hutool-json/src/main/java/cn/hutool/json/JSONParser.java @@ -2,6 +2,7 @@ package cn.hutool.json; import cn.hutool.core.lang.mutable.Mutable; import cn.hutool.core.lang.mutable.MutableEntry; +import cn.hutool.core.util.CharUtil; import java.util.function.Predicate; @@ -80,7 +81,7 @@ public class JSONParser { switch (tokener.nextClean()) { case ';': - case ',': + case CharUtil.COMMA: if (tokener.nextClean() == '}') { // issue#2380 // 尾后逗号(Trailing Commas),JSON中虽然不支持,但是ECMAScript 2017支持,此处做兼容。 @@ -111,7 +112,7 @@ public class JSONParser { if (x.nextClean() != ']') { x.back(); for (; ; ) { - if (x.nextClean() == ',') { + if (x.nextClean() == CharUtil.COMMA) { x.back(); jsonArray.addRaw(null, predicate); } else { @@ -119,7 +120,7 @@ public class JSONParser { jsonArray.addRaw(x.nextValue(), predicate); } switch (x.nextClean()) { - case ',': + case CharUtil.COMMA: if (x.nextClean() == ']') { return; } diff --git a/hutool-json/src/main/java/cn/hutool/json/JSONTokener.java b/hutool-json/src/main/java/cn/hutool/json/JSONTokener.java index 22bd82894..0a9d43fe0 100755 --- a/hutool-json/src/main/java/cn/hutool/json/JSONTokener.java +++ b/hutool-json/src/main/java/cn/hutool/json/JSONTokener.java @@ -71,6 +71,7 @@ public class JSONTokener { * * @param inputStream InputStream * @param config JSON配置 + * @throws JSONException JSON异常,包装IO异常 */ public JSONTokener(final InputStream inputStream, final JSONConfig config) throws JSONException { this(IoUtil.getUtf8Reader(inputStream), config); @@ -89,6 +90,8 @@ public class JSONTokener { /** * 将标记回退到第一个字符,重新开始解析新的JSON + * + * @throws JSONException JSON异常,包装IO异常 */ public void back() throws JSONException { if (this.usePrevious || this.index <= 0) { @@ -111,6 +114,7 @@ public class JSONTokener { * 源字符串是否有更多的字符 * * @return 如果未达到结尾返回true,否则false + * @throws JSONException JSON异常,包装IO异常 */ public boolean more() throws JSONException { this.next(); @@ -272,11 +276,11 @@ public class JSONTokener { } /** - * Get the text up but not including the specified character or the end of line, whichever comes first.
* 获得从当前位置直到分隔符(不包括分隔符)或行尾的的所有字符。 * * @param delimiter 分隔符 * @return 字符串 + * @throws JSONException JSON异常,包装IO异常 */ public String nextTo(final char delimiter) throws JSONException { final StringBuilder sb = new StringBuilder(); @@ -297,6 +301,7 @@ public class JSONTokener { * * @param delimiters A set of delimiter characters. * @return A string, trimmed. + * @throws JSONException JSON异常,包装IO异常 */ public String nextTo(final String delimiters) throws JSONException { char c; @@ -314,9 +319,9 @@ public class JSONTokener { } /** - * 获得下一个值,值类型可以是Boolean, Double, Integer, JSONArray, JSONObject, Long, or String, or the JSONObject.NULL + * 获得下一个值,值类型可以是Boolean, Double, Integer, JSONArray, JSONObject, Long, or String * - * @return Boolean, Double, Integer, JSONArray, JSONObject, Long, or String, or the JSONObject.NULL + * @return Boolean, Double, Integer, JSONArray, JSONObject, Long, or String * @throws JSONException 语法错误 */ public Object nextValue() throws JSONException { @@ -360,6 +365,7 @@ public class JSONTokener { * * @param to 需要定位的字符 * @return 定位的字符,如果字符未找到返回0 + * @throws JSONException IO异常 */ public char skipTo(final char to) throws JSONException { char c; @@ -378,8 +384,8 @@ public class JSONTokener { return c; } } while (c != to); - } catch (final IOException exception) { - throw new JSONException(exception); + } catch (final IOException e) { + throw new JSONException(e); } this.back(); return c; @@ -396,43 +402,6 @@ public class JSONTokener { return new JSONException(message + this); } - /** - * 转为 {@link JSONArray} - * - * @return {@link JSONArray} - */ - public JSONArray toJSONArray() { - final JSONArray jsonArray = new JSONArray(this.config); - if (this.nextClean() != '[') { - throw this.syntaxError("A JSONArray text must start with '['"); - } - if (this.nextClean() != ']') { - this.back(); - while (true) { - if (this.nextClean() == ',') { - this.back(); - jsonArray.add(null); - } else { - this.back(); - jsonArray.add(this.nextValue()); - } - switch (this.nextClean()) { - case ',': - if (this.nextClean() == ']') { - return jsonArray; - } - this.back(); - break; - case ']': - return jsonArray; - default: - throw this.syntaxError("Expected a ',' or ']'"); - } - } - } - return jsonArray; - } - /** * Make a printable string of this JSONTokener. * diff --git a/hutool-json/src/main/java/cn/hutool/json/JSONUtil.java b/hutool-json/src/main/java/cn/hutool/json/JSONUtil.java index 4f52ded37..55d71d46b 100755 --- a/hutool-json/src/main/java/cn/hutool/json/JSONUtil.java +++ b/hutool-json/src/main/java/cn/hutool/json/JSONUtil.java @@ -10,12 +10,11 @@ import cn.hutool.json.serialize.GlobalSerializeMapping; import cn.hutool.json.serialize.JSONArraySerializer; import cn.hutool.json.serialize.JSONDeserializer; import cn.hutool.json.serialize.JSONObjectSerializer; +import cn.hutool.json.writer.JSONValueWriter; +import cn.hutool.json.writer.JSONWriter; import cn.hutool.json.xml.JSONXMLUtil; -import java.io.File; -import java.io.IOException; -import java.io.Reader; -import java.io.Writer; +import java.io.*; import java.lang.reflect.Type; import java.nio.charset.Charset; import java.util.List; @@ -296,7 +295,18 @@ public class JSONUtil { * @return JSON字符串 * @since 5.7.12 */ + @SuppressWarnings({"unchecked", "rawtypes"}) public static String toJsonStr(final Object obj, final JSONConfig jsonConfig) { + // 自定义规则,优先级高于全局规则 + final JSONValueWriter valueWriter = InternalJSONUtil.getValueWriter(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/cn/hutool/json/convert/JSONConverter.java b/hutool-json/src/main/java/cn/hutool/json/convert/JSONConverter.java index a7badde32..253258b0a 100644 --- a/hutool-json/src/main/java/cn/hutool/json/convert/JSONConverter.java +++ b/hutool-json/src/main/java/cn/hutool/json/convert/JSONConverter.java @@ -2,39 +2,33 @@ package cn.hutool.json.convert; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.copier.BeanCopier; +import cn.hutool.core.convert.Convert; import cn.hutool.core.convert.ConvertException; import cn.hutool.core.convert.Converter; import cn.hutool.core.convert.RegisterConverter; -import cn.hutool.core.convert.impl.ArrayConverter; -import cn.hutool.core.convert.impl.CollectionConverter; -import cn.hutool.core.convert.impl.MapConverter; +import cn.hutool.core.convert.impl.*; import cn.hutool.core.map.MapWrapper; import cn.hutool.core.reflect.ConstructorUtil; import cn.hutool.core.reflect.TypeReference; import cn.hutool.core.reflect.TypeUtil; import cn.hutool.core.text.StrUtil; import cn.hutool.core.util.ArrayUtil; -import cn.hutool.json.InternalJSONUtil; -import cn.hutool.json.JSON; -import cn.hutool.json.JSONArray; -import cn.hutool.json.JSONConfig; -import cn.hutool.json.JSONException; -import cn.hutool.json.JSONObject; -import cn.hutool.json.JSONUtil; -import cn.hutool.json.serialize.GlobalSerializeMapping; +import cn.hutool.json.*; import cn.hutool.json.serialize.JSONDeserializer; +import cn.hutool.json.serialize.JSONString; import java.lang.reflect.Type; +import java.time.temporal.TemporalAccessor; import java.util.Collection; +import java.util.Date; import java.util.Iterator; import java.util.Map; /** * JSON转换器,实现Object对象转换为{@link JSON},支持的对象: *
    - *
  • String: 转换为相应的对象
  • - *
  • Array、Iterable、Iterator:转换为JSONArray
  • - *
  • Bean对象:转为JSONObject
  • + *
  • 任意支持的对象,转换为JSON
  • + *
  • JSOn转换为指定对象Bean
  • *
* * @author looly @@ -73,27 +67,40 @@ public class JSONConverter implements Converter { } @Override - public Object convert(Type targetType, final Object obj) throws ConvertException { - if (null == obj) { + public Object convert(Type targetType, Object value) throws ConvertException { + if (null == value) { return null; } - - // 对象转JSON - if (targetType instanceof JSON) { - return toJSON(obj); + if (value instanceof JSONString) { + // 被JSONString包装的对象,获取其原始类型 + value = ((JSONString) value).getRaw(); } // JSON转对象 - if (obj instanceof JSON) { + if (value instanceof JSON) { if (targetType instanceof TypeReference) { + // 还原原始类型 targetType = ((TypeReference) targetType).getType(); } - return toBean(targetType, (JSON) obj); + return toBean(targetType, (JSON) value); } - // 无法转换 - throw new JSONException("Can not convert from {}: [{}] to [{}]", - obj.getClass().getName(), obj, targetType.getTypeName()); + // 对象转JSON + final Class targetClass = TypeUtil.getClass(targetType); + if(null != targetClass){ + if (JSON.class.isAssignableFrom(targetClass)) { + return toJSON(value); + } + // 自定义日期格式 + if(Date.class.isAssignableFrom(targetClass) || TemporalAccessor.class.isAssignableFrom(targetClass)){ + final Object date = toDateWithFormat(targetClass, value); + if(null != date){ + return date; + } + } + } + + return Convert.convertWithCheck(targetType, value, null, config.isIgnoreError()); } /** @@ -127,22 +134,30 @@ public class JSONConverter implements Converter { return json; } + // ----------------------------------------------------------- Private method start + + /** + * JSON转Bean + * + * @param 目标类型 + * @param targetType 目标类型, + * @param json JSON + * @return bean + */ @SuppressWarnings("unchecked") private T toBean(final Type targetType, final JSON json) { - final Class rawType = (Class) TypeUtil.getClass(targetType); - if(null != rawType && JSONDeserializer.class.isAssignableFrom(rawType)){ - return (T) JSONDeserializerConverter.INSTANCE.convert(targetType, json); - } - // 全局自定义反序列化(优先级低于实现JSONDeserializer接口) - final JSONDeserializer deserializer = GlobalSerializeMapping.getDeserializer(targetType); + // 自定义对象反序列化 + final JSONDeserializer deserializer = InternalJSONUtil.getDeserializer(targetType); if (null != deserializer) { return (T) deserializer.deserialize(json); } - // 其他转换不支持非Class的泛型类型 + final Class rawType = (Class) TypeUtil.getClass(targetType); if (null == rawType) { - throw new JSONException("Can not get class from type: {}", targetType); + // 当目标类型不确定时,返回原JSON + return (T) json; + //throw new JSONException("Can not get class from type: {}", targetType); } // 特殊类型转换,包括Collection、Map、强转、Array等 final T result = toSpecial(targetType, rawType, json); @@ -164,7 +179,7 @@ public class JSONConverter implements Converter { } // 跳过异常时返回null - if(json.getConfig().isIgnoreError()){ + if (json.getConfig().isIgnoreError()) { return null; } @@ -173,8 +188,6 @@ public class JSONConverter implements Converter { json.getClass().getName(), json, targetType.getTypeName()); } - // ----------------------------------------------------------- Private method start - /** * 特殊类型转换
* 包括: @@ -220,5 +233,18 @@ public class JSONConverter implements Converter { // 表示非需要特殊转换的对象 return null; } + + private Object toDateWithFormat(final Class targetClass, final Object value){ + // 日期转换,支持自定义日期格式 + final String format = config.getDateFormat(); + if (StrUtil.isNotBlank(format)) { + if (Date.class.isAssignableFrom(targetClass)) { + return new DateConverter(format).convert(targetClass, value); + } else { + return new TemporalAccessorConverter(format).convert(targetClass, value); + } + } + return null; + } // ----------------------------------------------------------- Private method end } diff --git a/hutool-json/src/main/java/cn/hutool/json/convert/JSONDeserializerConverter.java b/hutool-json/src/main/java/cn/hutool/json/convert/JSONDeserializerConverter.java deleted file mode 100644 index 9265a100a..000000000 --- a/hutool-json/src/main/java/cn/hutool/json/convert/JSONDeserializerConverter.java +++ /dev/null @@ -1,36 +0,0 @@ -package cn.hutool.json.convert; - -import cn.hutool.core.convert.AbstractConverter; -import cn.hutool.core.convert.ConvertException; -import cn.hutool.core.reflect.ConstructorUtil; -import cn.hutool.json.JSON; -import cn.hutool.json.serialize.JSONDeserializer; - -/** - * 实现了{@link JSONDeserializer}接口的Bean对象转换器,用于将指定JSON转换为JSONDeserializer子对象。 - * - * @author looly - * @since 6.0.0 - */ -public class JSONDeserializerConverter extends AbstractConverter { - private static final long serialVersionUID = 1L; - - /** - * 单例 - */ - public static final JSONDeserializerConverter INSTANCE = new JSONDeserializerConverter(); - - @Override - protected Object convertInternal(final Class targetClass, final Object value) { - // 自定义反序列化 - if (value instanceof JSON) { - final JSONDeserializer target = (JSONDeserializer) ConstructorUtil.newInstanceIfPossible(targetClass); - if (null == target) { - throw new ConvertException("Can not instance target: [{}]", targetClass); - } - return target.deserialize((JSON) value); - } - - throw new ConvertException("JSONDeserializer bean must be convert from JSON!"); - } -} diff --git a/hutool-json/src/main/java/cn/hutool/json/serialize/GlobalSerializeMapping.java b/hutool-json/src/main/java/cn/hutool/json/serialize/GlobalSerializeMapping.java index 3ccad807d..753cf4c41 100644 --- a/hutool-json/src/main/java/cn/hutool/json/serialize/GlobalSerializeMapping.java +++ b/hutool-json/src/main/java/cn/hutool/json/serialize/GlobalSerializeMapping.java @@ -1,6 +1,8 @@ package cn.hutool.json.serialize; import cn.hutool.core.map.SafeConcurrentHashMap; +import cn.hutool.core.reflect.NullType; +import cn.hutool.core.util.ObjUtil; import cn.hutool.json.JSON; import java.lang.reflect.Type; @@ -59,19 +61,6 @@ public class GlobalSerializeMapping { putInternal(type, serializer); } - /** - * 加入自定义的序列化器 - * - * @param type 对象类型 - * @param serializer 序列化器实现 - */ - synchronized private static void putInternal(final Type type, final JSONSerializer serializer) { - if (null == serializerMap) { - serializerMap = new ConcurrentHashMap<>(); - } - serializerMap.put(type, serializer); - } - /** * 加入自定义的反序列化器 * @@ -82,7 +71,7 @@ public class GlobalSerializeMapping { if (null == deserializerMap) { deserializerMap = new ConcurrentHashMap<>(); } - deserializerMap.put(type, deserializer); + deserializerMap.put(ObjUtil.defaultIfNull(type, NullType.INSTANCE), deserializer); } /** @@ -95,7 +84,7 @@ public class GlobalSerializeMapping { if (null == serializerMap) { return null; } - return serializerMap.get(type); + return serializerMap.get(ObjUtil.defaultIfNull(type, NullType.INSTANCE)); } /** @@ -108,6 +97,19 @@ public class GlobalSerializeMapping { if (null == deserializerMap) { return null; } - return deserializerMap.get(type); + return deserializerMap.get(ObjUtil.defaultIfNull(type, NullType.INSTANCE)); + } + + /** + * 加入自定义的序列化器 + * + * @param type 对象类型 + * @param serializer 序列化器实现 + */ + synchronized private static void putInternal(final Type type, final JSONSerializer serializer) { + if (null == serializerMap) { + serializerMap = new ConcurrentHashMap<>(); + } + serializerMap.put(ObjUtil.defaultIfNull(type, NullType.INSTANCE), serializer); } } diff --git a/hutool-json/src/main/java/cn/hutool/json/serialize/package-info.java b/hutool-json/src/main/java/cn/hutool/json/serialize/package-info.java new file mode 100644 index 000000000..7adc2a7b8 --- /dev/null +++ b/hutool-json/src/main/java/cn/hutool/json/serialize/package-info.java @@ -0,0 +1,4 @@ +/** + * JSON序列化和反序列化,提供对象和JSON之间的转换 + */ +package cn.hutool.json.serialize; diff --git a/hutool-json/src/main/java/cn/hutool/json/writer/GlobalValueWriterMapping.java b/hutool-json/src/main/java/cn/hutool/json/writer/GlobalValueWriterMapping.java new file mode 100644 index 000000000..d36d5e802 --- /dev/null +++ b/hutool-json/src/main/java/cn/hutool/json/writer/GlobalValueWriterMapping.java @@ -0,0 +1,44 @@ +package cn.hutool.json.writer; + +import cn.hutool.core.map.SafeConcurrentHashMap; +import cn.hutool.core.reflect.NullType; +import cn.hutool.core.util.ObjUtil; + +import java.lang.reflect.Type; +import java.util.Map; + +/** + * 全局自定义对象写出
+ * 用户通过此全局定义,可针对某些特殊对象 + * + * @author looly + * @since 6.0.0 + */ +public class GlobalValueWriterMapping { + + private static final Map> writerMap; + + static { + writerMap = new SafeConcurrentHashMap<>(); + } + + /** + * 加入自定义的对象值写出规则 + * + * @param type 对象类型 + * @param writer 自定义对象写出实现 + */ + public static void put(final Type type, final JSONValueWriter writer) { + writerMap.put(ObjUtil.defaultIfNull(type, NullType.INSTANCE), writer); + } + + /** + * 获取自定义对象值写出规则 + * + * @param type 对象类型 + * @return 自定义的 {@link JSONValueWriter} + */ + public static JSONValueWriter get(final Type type) { + return writerMap.get(ObjUtil.defaultIfNull(type, NullType.INSTANCE)); + } +} diff --git a/hutool-json/src/main/java/cn/hutool/json/writer/JSONValueWriter.java b/hutool-json/src/main/java/cn/hutool/json/writer/JSONValueWriter.java index cd3c7b722..e35f9420b 100755 --- a/hutool-json/src/main/java/cn/hutool/json/writer/JSONValueWriter.java +++ b/hutool-json/src/main/java/cn/hutool/json/writer/JSONValueWriter.java @@ -1,5 +1,7 @@ package cn.hutool.json.writer; +import java.io.IOException; + /** * JSON的值自定义写出 * @@ -14,6 +16,7 @@ public interface JSONValueWriter { * * @param writer {@link JSONWriter} * @param value 被写出的值 + * @throws IOException IO异常 */ void write(JSONWriter writer, T value); } diff --git a/hutool-json/src/main/java/cn/hutool/json/writer/JSONWriter.java b/hutool-json/src/main/java/cn/hutool/json/writer/JSONWriter.java index d6e1e93c5..b3b99d021 100755 --- a/hutool-json/src/main/java/cn/hutool/json/writer/JSONWriter.java +++ b/hutool-json/src/main/java/cn/hutool/json/writer/JSONWriter.java @@ -4,6 +4,7 @@ import cn.hutool.core.io.IORuntimeException; import cn.hutool.core.lang.mutable.MutableEntry; import cn.hutool.core.text.StrUtil; import cn.hutool.core.util.CharUtil; +import cn.hutool.core.util.ObjUtil; import cn.hutool.json.InternalJSONUtil; import cn.hutool.json.JSON; import cn.hutool.json.JSONConfig; @@ -76,7 +77,7 @@ public class JSONWriter extends Writer { this.writer = writer; this.indentFactor = indentFactor; this.indent = indent; - this.config = config; + this.config = ObjUtil.defaultIfNull(config, JSONConfig.of()); } /** @@ -298,12 +299,21 @@ public class JSONWriter extends Writer { * @param predicate 过滤修改器 * @return this */ + @SuppressWarnings({"unchecked", "rawtypes"}) private JSONWriter writeObjValue(final Object value, final Predicate> predicate) { final int indent = indentFactor + this.indent; + + // 自定义规则 + final JSONValueWriter valueWriter = InternalJSONUtil.getValueWriter(value); + if(null != valueWriter){ + valueWriter.write(this, value); + return this; + } + if (value == null) { //noinspection resource writeRaw(StrUtil.NULL); - } else if (value instanceof JSON) { + }else if (value instanceof JSON) { ((JSON) value).write(writer, indentFactor, indent, predicate); } else if (value instanceof Number) { NumberValueWriter.INSTANCE.write(this, (Number) value); diff --git a/hutool-json/src/test/java/cn/hutool/json/writer/GlobalValueWriterMappingTest.java b/hutool-json/src/test/java/cn/hutool/json/writer/GlobalValueWriterMappingTest.java new file mode 100644 index 000000000..2243a32f5 --- /dev/null +++ b/hutool-json/src/test/java/cn/hutool/json/writer/GlobalValueWriterMappingTest.java @@ -0,0 +1,67 @@ +package cn.hutool.json.writer; + +import cn.hutool.core.convert.Converter; +import cn.hutool.json.JSONConfig; +import cn.hutool.json.JSONUtil; +import lombok.Data; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +public class GlobalValueWriterMappingTest { + + @Before + public void init(){ + GlobalValueWriterMapping.put(CustomSubBean.class, (JSONValueWriter) (writer, value) -> { + writer.writeRaw(String.valueOf(value.getId())); + }); + } + + @Test + public void customWriteTest(){ + final CustomSubBean customBean = new CustomSubBean(); + customBean.setId(12); + customBean.setName("aaa"); + final String s = JSONUtil.toJsonStr(customBean); + Assert.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); + Assert.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); + Assert.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; + } +}