diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/InternalJSONUtil.java b/hutool-json/src/main/java/org/dromara/hutool/json/InternalJSONUtil.java index 0b59e5da8..454e6413d 100644 --- a/hutool-json/src/main/java/org/dromara/hutool/json/InternalJSONUtil.java +++ b/hutool-json/src/main/java/org/dromara/hutool/json/InternalJSONUtil.java @@ -12,6 +12,7 @@ package org.dromara.hutool.json; +import org.dromara.hutool.core.array.ArrayUtil; import org.dromara.hutool.core.bean.copier.CopyOptions; import org.dromara.hutool.core.codec.HexUtil; import org.dromara.hutool.core.convert.Convert; @@ -20,25 +21,22 @@ import org.dromara.hutool.core.lang.mutable.MutableEntry; import org.dromara.hutool.core.map.CaseInsensitiveLinkedMap; import org.dromara.hutool.core.map.CaseInsensitiveTreeMap; import org.dromara.hutool.core.math.NumberUtil; -import org.dromara.hutool.core.reflect.ClassUtil; import org.dromara.hutool.core.reflect.ConstructorUtil; import org.dromara.hutool.core.reflect.TypeUtil; +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.array.ArrayUtil; -import org.dromara.hutool.core.text.CharUtil; -import org.dromara.hutool.core.util.ObjUtil; +import org.dromara.hutool.json.mapper.JSONValueMapper; import org.dromara.hutool.json.serialize.GlobalSerializeMapping; import org.dromara.hutool.json.serialize.JSONDeserializer; import org.dromara.hutool.json.serialize.JSONStringer; import org.dromara.hutool.json.writer.GlobalValueWriterMapping; import org.dromara.hutool.json.writer.JSONValueWriter; -import java.io.*; +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.*; import java.util.function.Predicate; @@ -68,61 +66,7 @@ public final class InternalJSONUtil { * @return 包装后的值,null表示此值需被忽略 */ static Object wrap(final Object object, final JSONConfig jsonConfig) { - // null和自定义对象原样存储 - if (null == object || null != InternalJSONUtil.getValueWriter(object)) { - return object; - } - - if (object instanceof JSON // - || object instanceof JSONStringer // - || object instanceof CharSequence // - || object instanceof Number // - || ObjUtil.isBasicType(object) // - ) { - if (!ObjUtil.isValidIfNumber(object)) { - throw new JSONException("JSON does not allow non-finite numbers."); - } - return object; - } - - try { - // fix issue#1399@Github - if (object instanceof SQLException) { - return object.toString(); - } - - // JSONArray - if (object instanceof Iterable || ArrayUtil.isArray(object)) { - return new JSONArray(object, jsonConfig); - } - // JSONObject - if (object instanceof Map || object instanceof Map.Entry) { - return new JSONObject(object, jsonConfig); - } - - // 日期类型做包装,以便自定义输出格式 - if (object instanceof Date - || object instanceof Calendar - || object instanceof TemporalAccessor - ) { - // 日期类型保存原始类型,用于在writer时自定义转字符串 - return object; - } - // 枚举类保存其字符串形式(4.0.2新增) - if (object instanceof Enum) { - return object.toString(); - } - - // Java内部类不做转换 - if (ClassUtil.isJdkClass(object.getClass())) { - return object.toString(); - } - - // 默认按照JSONObject对待 - return new JSONObject(object, jsonConfig); - } catch (final Exception exception) { - return null; - } + return JSONValueMapper.of(jsonConfig).map(object); } /** @@ -167,51 +111,6 @@ public final class InternalJSONUtil { } } - /** - * 尝试转换字符串为number, boolean, or null,无法转换返回String - * - * @param string A String. - * @return A simple JSON value. - */ - public static Object stringToValue(final String string) { - // null处理 - if (StrUtil.isEmpty(string) || StrUtil.NULL.equalsIgnoreCase(string)) { - return null; - } - - // boolean处理 - if ("true".equalsIgnoreCase(string)) { - return Boolean.TRUE; - } - if ("false".equalsIgnoreCase(string)) { - return Boolean.FALSE; - } - - // Number处理 - final char b = string.charAt(0); - if ((b >= '0' && b <= '9') || b == '-') { - try { - if (StrUtil.containsAnyIgnoreCase(string, ".", "e")) { - // pr#192@Gitee,Double会出现小数精度丢失问题,此处使用BigDecimal - return new BigDecimal(string); - } else { - final long myLong = Long.parseLong(string); - if (string.equals(Long.toString(myLong))) { - if (myLong == (int) myLong) { - return (int) myLong; - } else { - return myLong; - } - } - } - } catch (final Exception ignore) { - } - } - - // 其它情况返回原String值下 - return string; - } - /** * 将Property的键转化为JSON形式
* 用于识别类似于:org.dromara.hutool.json这类用点隔开的键
diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/JSONTokener.java b/hutool-json/src/main/java/org/dromara/hutool/json/JSONTokener.java index 44fc33d88..5daad54a8 100644 --- a/hutool-json/src/main/java/org/dromara/hutool/json/JSONTokener.java +++ b/hutool-json/src/main/java/org/dromara/hutool/json/JSONTokener.java @@ -15,6 +15,7 @@ package org.dromara.hutool.json; import org.dromara.hutool.core.io.IoUtil; import org.dromara.hutool.core.io.ReaderWrapper; import org.dromara.hutool.core.text.StrUtil; +import org.dromara.hutool.json.mapper.JSONValueMapper; import java.io.IOException; import java.io.InputStream; @@ -56,6 +57,7 @@ public class JSONTokener extends ReaderWrapper { private final JSONConfig config; // ------------------------------------------------------------------------------------ Constructor start + /** * 从InputStream中构建,使用UTF-8编码 * @@ -342,6 +344,7 @@ public class JSONTokener extends ReaderWrapper { /** * 获得下一个值,值类型可以是Boolean, Double, Integer, JSONArray, JSONObject, Long, or String * + * @param getOnlyStringValue 是否只获取String值 * @return Boolean, Double, Integer, JSONArray, JSONObject, Long, or String * @throws JSONException 语法错误 */ @@ -352,7 +355,7 @@ public class JSONTokener extends ReaderWrapper { case '\'': return this.nextString(c); case '{': - if(getOnlyStringValue){ + if (getOnlyStringValue) { throw this.syntaxError("String value must not begin with '{'"); } this.back(); @@ -362,7 +365,7 @@ public class JSONTokener extends ReaderWrapper { throw new JSONException("JSONObject depth too large to process.", e); } case '[': - if(getOnlyStringValue){ + if (getOnlyStringValue) { throw this.syntaxError("String value must not begin with '['"); } this.back(); @@ -390,7 +393,7 @@ public class JSONTokener extends ReaderWrapper { if (valueString.isEmpty()) { throw this.syntaxError("Missing value"); } - return getOnlyStringValue ? valueString : InternalJSONUtil.stringToValue(valueString); + return getOnlyStringValue ? valueString : JSONValueMapper.toJsonValue(valueString); } /** diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/mapper/JSONValueMapper.java b/hutool-json/src/main/java/org/dromara/hutool/json/mapper/JSONValueMapper.java new file mode 100644 index 000000000..981cbf407 --- /dev/null +++ b/hutool-json/src/main/java/org/dromara/hutool/json/mapper/JSONValueMapper.java @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2023. looly(loolly@aliyun.com) + * Hutool is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * https://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.dromara.hutool.json.mapper; + +import org.dromara.hutool.core.array.ArrayUtil; +import org.dromara.hutool.core.math.NumberUtil; +import org.dromara.hutool.core.reflect.ClassUtil; +import org.dromara.hutool.core.text.StrUtil; +import org.dromara.hutool.core.util.ObjUtil; +import org.dromara.hutool.json.*; +import org.dromara.hutool.json.serialize.JSONStringer; + +import java.math.BigDecimal; +import java.sql.SQLException; +import java.time.temporal.TemporalAccessor; +import java.util.Calendar; +import java.util.Date; +import java.util.Map; + +/** + * 对象和JSON值映射器,用于转换对象为JSON对象中的值
+ * 有效的JSON值包括: + * + * + * @author looly + * @since 6.0.0 + */ +public class JSONValueMapper { + + /** + * 创建ObjectMapper + * + * @param jsonConfig 来源对象 + * @return ObjectMapper + */ + public static JSONValueMapper of(final JSONConfig jsonConfig) { + return new JSONValueMapper(jsonConfig); + } + + /** + * 尝试转换字符串为number, boolean, or null,无法转换返回String
+ * 此方法用于解析JSON字符串时,将字符串中的值转换为JSON值对象 + * + * @param string A String. + * @return A simple JSON value. + */ + public static Object toJsonValue(final String string) { + // null处理 + if (StrUtil.isEmpty(string) || StrUtil.NULL.equalsIgnoreCase(string)) { + return null; + } + + // boolean处理 + if ("true".equalsIgnoreCase(string)) { + return Boolean.TRUE; + } + if ("false".equalsIgnoreCase(string)) { + return Boolean.FALSE; + } + + // Number处理 + final char b = string.charAt(0); + if ((b >= '0' && b <= '9') || b == '-') { + try { + if (StrUtil.containsAnyIgnoreCase(string, ".", "e")) { + // pr#192@Gitee,Double会出现小数精度丢失问题,此处使用BigDecimal + return new BigDecimal(string); + } else { + final long myLong = Long.parseLong(string); + if (string.equals(Long.toString(myLong))) { + if (myLong == (int) myLong) { + return (int) myLong; + } else { + return myLong; + } + } + } + } catch (final Exception ignore) { + } + } + + // 其它情况返回原String值下 + return string; + } + + private final JSONConfig jsonConfig; + + /** + * 构造 + * + * @param jsonConfig JSON配置 + */ + public JSONValueMapper(final JSONConfig jsonConfig) { + this.jsonConfig = jsonConfig; + } + + /** + * 在需要的时候转换映射对象
+ * 包装包括: + * + * + * @param object 被映射的对象 + * @return 映射后的值,null表示此值需被忽略 + */ + public Object map(final Object object) { + // null、JSON、字符串和自定义对象原样存储 + if (null == object + || null != InternalJSONUtil.getValueWriter(object) + || object instanceof JSON // + || object instanceof JSONStringer // + || object instanceof CharSequence // + || ObjUtil.isBasicType(object) // + ) { + return object; + } + + // 合法数字原样存储 + if(object instanceof Number){ + if(!NumberUtil.isValidNumber((Number) object)){ + throw new JSONException("JSON does not allow non-finite numbers."); + } + return object; + } + + // 日期类型做包装,以便自定义输出格式 + if (object instanceof Date + || object instanceof Calendar + || object instanceof TemporalAccessor + ) { + // 日期类型保存原始类型,用于在writer时自定义转字符串 + return object; + } + + // 特定对象转换 + try { + // fix issue#1399@Github + if (object instanceof SQLException + ) { + return object.toString(); + } + + // JSONArray + if (object instanceof Iterable || ArrayUtil.isArray(object)) { + return new JSONArray(object, jsonConfig); + } + // JSONObject + if (object instanceof Map || object instanceof Map.Entry) { + return new JSONObject(object, jsonConfig); + } + + // 枚举类保存其字符串形式(4.0.2新增) + if (object instanceof Enum + // Java内部类不做转换 + || ClassUtil.isJdkClass(object.getClass()) + ) { + return object.toString(); + } + + // 默认按照JSONObject对待 + return new JSONObject(object, jsonConfig); + } catch (final Exception exception) { + return null; + } + } +} diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/xml/JSONXMLParser.java b/hutool-json/src/main/java/org/dromara/hutool/json/xml/JSONXMLParser.java index ca14ff86b..addf574d7 100644 --- a/hutool-json/src/main/java/org/dromara/hutool/json/xml/JSONXMLParser.java +++ b/hutool-json/src/main/java/org/dromara/hutool/json/xml/JSONXMLParser.java @@ -13,9 +13,9 @@ package org.dromara.hutool.json.xml; import org.dromara.hutool.core.text.StrUtil; -import org.dromara.hutool.json.InternalJSONUtil; import org.dromara.hutool.json.JSONException; import org.dromara.hutool.json.JSONObject; +import org.dromara.hutool.json.mapper.JSONValueMapper; /** * XML解析器,将XML解析为JSON对象 @@ -73,7 +73,7 @@ public class JSONXMLParser { if ("CDATA".equals(token)) { if (x.next() == '[') { string = x.nextCDATA(); - if (string.length() > 0) { + if (!string.isEmpty()) { context.append("content", string); } return false; @@ -137,7 +137,7 @@ public class JSONXMLParser { if (!(token instanceof String)) { throw x.syntaxError("Missing value"); } - jsonobject.append(string, keepStrings ? token : InternalJSONUtil.stringToValue((String) token)); + jsonobject.append(string, keepStrings ? token : JSONValueMapper.toJsonValue((String) token)); token = null; } else { jsonobject.append(string, ""); @@ -148,7 +148,7 @@ public class JSONXMLParser { if (x.nextToken() != JSONXMLUtil.GT) { throw x.syntaxError("Misshaped tag"); } - if (jsonobject.size() > 0) { + if (!jsonobject.isEmpty()) { context.append(tagName, jsonobject); } else { context.append(tagName, StrUtil.EMPTY); @@ -166,15 +166,15 @@ public class JSONXMLParser { return false; } else if (token instanceof String) { string = (String) token; - if (string.length() > 0) { - jsonobject.append("content", keepStrings ? token : InternalJSONUtil.stringToValue(string)); + if (!string.isEmpty()) { + jsonobject.append("content", keepStrings ? token : JSONValueMapper.toJsonValue(string)); } } else if (token == JSONXMLUtil.LT) { // Nested element if (parse(x, jsonobject, tagName, keepStrings)) { - if (jsonobject.size() == 0) { - context.append(tagName, ""); + if (jsonobject.isEmpty()) { + context.append(tagName, StrUtil.EMPTY); } else if (jsonobject.size() == 1 && jsonobject.get("content") != null) { context.append(tagName, jsonobject.get("content")); } else {