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