From 463d8d9c6f93e42958b7c01018202048151895f7 Mon Sep 17 00:00:00 2001 From: Looly Date: Tue, 1 Oct 2024 22:14:02 +0800 Subject: [PATCH] add methods --- .../dromara/hutool/json/InternalJSONUtil.java | 2 +- .../org/dromara/hutool/json/JSONArray.java | 6 +- .../org/dromara/hutool/json/JSONFactory.java | 33 ++++-- .../org/dromara/hutool/json/JSONObject.java | 3 +- .../org/dromara/hutool/json/JSONUtil.java | 112 +++++++++++++----- .../hutool/json/serializer/JSONMapper.java | 49 +++++--- .../impl/CharSequenceTypeAdapter.java | 14 +-- .../dromara/hutool/json/JSONFactoryTest.java | 36 ++++++ .../hutool/json/issues/Issue3681Test.java | 5 +- .../hutool/json/issues/IssueI9DX5HTest.java | 2 +- 10 files changed, 191 insertions(+), 71 deletions(-) create mode 100644 hutool-json/src/test/java/org/dromara/hutool/json/JSONFactoryTest.java 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 b299d805c..d3d769dc0 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 @@ -117,7 +117,7 @@ public final class InternalJSONUtil { .setIgnoreError(config.isIgnoreError()) .setIgnoreNullValue(config.isIgnoreNullValue()) .setTransientSupport(config.isTransientSupport()) - .setConverter((targetType, value) -> mapper.toJSON(value)); + .setConverter((targetType, value) -> mapper.toJSON(value, false)); } /** diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/JSONArray.java b/hutool-json/src/main/java/org/dromara/hutool/json/JSONArray.java index 77ce2f478..895f517ae 100644 --- a/hutool-json/src/main/java/org/dromara/hutool/json/JSONArray.java +++ b/hutool-json/src/main/java/org/dromara/hutool/json/JSONArray.java @@ -120,7 +120,8 @@ public class JSONArray extends ListWrapper implements JSON, JSONGetter implements JSON, JSONGetter + *
  • {@link CharSequence},解析{...}字符串
  • + *
  • Bean解析
  • + *
  • Map解析
  • + * * * @param obj Bean对象或者Map * @return JSONObject @@ -292,23 +297,37 @@ public class JSONFactory { } /** - * 转换对象为JSON,如果用户不配置JSONConfig,则JSON的有序与否与传入对象有关。
    + * 解析对象为JSON
    * 支持的对象: *
      *
    • String: 转换为相应的对象
    • *
    • Array、Iterable、Iterator:转换为JSONArray
    • *
    • Bean对象:转为JSONObject
    • *
    + * 注意:与{@link #toJSON(Object)}不同的是,对象如果为字符串,会被当作json字符串解析! * * @param obj 对象 * @return JSON(JSONObject or JSONArray) */ public JSON parse(final Object obj) { - final JSONMapper mapper = this.getMapper(); - if (obj instanceof CharSequence) { - return mapper.toJSON((CharSequence) obj); - } - return mapper.toJSON(obj); + return getMapper().toJSON(obj, true); + } + + /** + * 转换对象为JSON
    + * 支持的对象: + *
      + *
    • String: 转换为{@link JSONPrimitive}
    • + *
    • Array、Iterable、Iterator:转换为JSONArray
    • + *
    • Bean对象:转为JSONObject
    • + *
    + * 注意;与{@link #parse(Object)}不同的是,对象如果为字符串,则返回{@link JSONPrimitive},不会做二次解析 + * + * @param obj 对象 + * @return JSON(JSONObject or JSONArray) + */ + public JSON toJSON(final Object obj) { + return getMapper().toJSON(obj, false); } // endregion diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/JSONObject.java b/hutool-json/src/main/java/org/dromara/hutool/json/JSONObject.java index d5694f896..188716ca0 100644 --- a/hutool-json/src/main/java/org/dromara/hutool/json/JSONObject.java +++ b/hutool-json/src/main/java/org/dromara/hutool/json/JSONObject.java @@ -233,7 +233,8 @@ public class JSONObject extends MapWrapper implements JSON, JSONGe * @throws JSONException 值是无穷数字抛出此异常 */ public JSONObject putObj(final String key, final Object value) throws JSONException { - this.put(key, factory.getMapper().toJSON(value)); + // put时,如果value为字符串,不解析,而是作为JSONPrimitive对待 + this.put(key, factory.getMapper().toJSON(value, false)); return this; } diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/JSONUtil.java b/hutool-json/src/main/java/org/dromara/hutool/json/JSONUtil.java index ed4e6aa96..898b949d9 100644 --- a/hutool-json/src/main/java/org/dromara/hutool/json/JSONUtil.java +++ b/hutool-json/src/main/java/org/dromara/hutool/json/JSONUtil.java @@ -172,8 +172,8 @@ public class JSONUtil { /** * JSON字符串转JSONArray * - * @param obj 数组或集合对象 - * @param config JSON配置 + * @param obj 数组或集合对象 + * @param config JSON配置 * @return JSONArray * @since 5.3.1 */ @@ -184,9 +184,9 @@ public class JSONUtil { /** * 对象转JSONArray * - * @param obj 数组或集合对象 - * @param config JSON配置 - * @param predicate index和值对过滤编辑器,可以通过实现此接口,完成解析前对键值对的过滤和修改操作,{@link Predicate#test(Object)}为{@code true}保留 + * @param obj 数组或集合对象 + * @param config JSON配置 + * @param predicate index和值对过滤编辑器,可以通过实现此接口,完成解析前对键值对的过滤和修改操作,{@link Predicate#test(Object)}为{@code true}保留 * @return JSONArray * @since 5.3.1 */ @@ -195,12 +195,12 @@ public class JSONUtil { } /** - * 转换对象为JSON,如果用户不配置JSONConfig,则JSON的有序与否与传入对象有关。
    - * 支持的对象: + * 解析对象为JSON,持的对象: *
      - *
    • String: 转换为相应的对象
    • - *
    • Array、Iterable、Iterator:转换为JSONArray
    • - *
    • Bean对象:转为JSONObject
    • + *
    • String: 解析为相应的对象
    • + *
    • Number、boolean: 转换为{@link JSONPrimitive}
    • + *
    • Array、Iterable、Iterator:转换为{@link JSONArray}
    • + *
    • Bean对象:转为{@link JSONObject}
    • *
    * * @param obj 对象 @@ -211,45 +211,46 @@ public class JSONUtil { } /** - * 转换对象为JSON,如果用户不配置JSONConfig,则JSON的有序与否与传入对象有关。
    - * 支持的对象: + * 解析对象为JSON,持的对象: *
      - *
    • String: 转换为相应的对象
    • - *
    • Array、Iterable、Iterator:转换为JSONArray
    • - *
    • Bean对象:转为JSONObject
    • + *
    • String: 解析为相应的对象
    • + *
    • Number、boolean: 转换为{@link JSONPrimitive}
    • + *
    • Array、Iterable、Iterator:转换为{@link JSONArray}
    • + *
    • Bean对象:转为{@link JSONObject}
    • *
    * * @param obj 对象 * @param config JSON配置,{@code null}使用默认配置 - * @return JSON(JSONObject or JSONArray) + * @return JSON */ public static JSON parse(final Object obj, final JSONConfig config) { return parse(obj, config, null); } /** - * 转换对象为JSON,如果用户不配置JSONConfig,则JSON的有序与否与传入对象有关。
    - * 支持的对象: + * 解析对象为JSON,持的对象: *
      - *
    • String: 转换为相应的对象
    • - *
    • Array、Iterable、Iterator:转换为JSONArray
    • - *
    • Bean对象:转为JSONObject
    • + *
    • String: 解析为相应的对象
    • + *
    • Number、boolean: 转换为{@link JSONPrimitive}
    • + *
    • Array、Iterable、Iterator:转换为{@link JSONArray}
    • + *
    • Bean对象:转为{@link JSONObject}
    • *
    * * @param obj 对象 * @param config JSON配置,{@code null}使用默认配置 - * @param predicate 键值对过滤编辑器,可以通过实现此接口,完成解析前对键值对的过滤和修改操作,{@link Predicate#test(Object)}为{@code true}保留 - * @return JSON(JSONObject or JSONArray) + * @param predicate 键值对过滤编辑器,可以通过实现此接口,完成解析前对键值对的过滤和修改操作, + * {@link Predicate#test(Object)}为{@code true}保留 + * @return JSON */ public static JSON parse(final Object obj, final JSONConfig config, final Predicate> predicate) { return JSONFactory.of(config, predicate).parse(obj); } /** - * XML字符串转为JSONObject + * XML字符串解析为{@link JSONObject} * * @param xmlStr XML字符串 - * @return JSONObject + * @return {@link JSONObject} */ public static JSONObject parseFromXml(final String xmlStr) { return JSONXMLUtil.toJSONObject(xmlStr); @@ -335,7 +336,7 @@ public class JSONUtil { /** * 转换为JSON字符串并写出到writer * - * @param obj 被转为JSON的对象 + * @param obj 被转为JSON的对象 * @param appendable {@link Appendable} * @since 5.3.3 */ @@ -389,7 +390,7 @@ public class JSONUtil { * 转为实体类对象 * * @param Bean类型 - * @param obj 对象 + * @param obj 对象 * @param type 实体类对象类型 * @return 实体类对象 */ @@ -418,7 +419,62 @@ public class JSONUtil { } // endregion + // region ----- toJSON + + /** + * 转换对象为JSON,如果用户不配置JSONConfig,则JSON的有序与否与传入对象有关。
    + * 支持的对象: + *
      + *
    • boolean、Number、String: 转换为{@link JSONPrimitive}/li> + *
    • Array、Iterable、Iterator:转换为{@link JSONArray}
    • + *
    • Bean对象:转为{@link JSONObject}
    • + *
    + * + * @param obj 对象 + * @return JSON + */ + public static JSON toJSON(final Object obj) { + return JSONFactory.getInstance().toJSON(obj); + } + + /** + * 转换对象为JSON,如果用户不配置JSONConfig,则JSON的有序与否与传入对象有关。
    + * 支持的对象: + *
      + *
    • boolean、Number、String: 转换为{@link JSONPrimitive}
    • + *
    • Array、Iterable、Iterator:转换为{@link JSONArray}
    • + *
    • Bean对象:转为{@link JSONObject}
    • + *
    + * + * @param obj 对象 + * @param config JSON配置,{@code null}使用默认配置 + * @return JSON + */ + public static JSON toJSON(final Object obj, final JSONConfig config) { + return toJSON(obj, config, null); + } + + /** + * 转换对象为JSON,如果用户不配置JSONConfig,则JSON的有序与否与传入对象有关。
    + * 支持的对象: + *
      + *
    • boolean、Number、String: 转换为{@link JSONPrimitive}
    • + *
    • Array、Iterable、Iterator:转换为{@link JSONArray}
    • + *
    • Bean对象:转为{@link JSONObject}
    • + *
    + * + * @param obj 对象 + * @param config JSON配置,{@code null}使用默认配置 + * @param predicate 键值对过滤编辑器,可以通过实现此接口,完成解析前对键值对的过滤和修改操作,{@link Predicate#test(Object)}为{@code true}保留 + * @return JSON + */ + public static JSON toJSON(final Object obj, final JSONConfig config, final Predicate> predicate) { + return JSONFactory.of(config, predicate).toJSON(obj); + } + // endregion + // region ----- toList + /** * 将JSONArray字符串转换为Bean的List,默认为ArrayList * @@ -601,6 +657,7 @@ public class JSONUtil { } // region ----- isType + /** * 是否为JSON类型字符串,首尾都为大括号或中括号判定为JSON字符串 * @@ -642,6 +699,7 @@ public class JSONUtil { // endregion // region ----- registerTypeAdapter + /** * 全局注册自定义类型适配器,用于自定义对象序列化和反序列化 * diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/serializer/JSONMapper.java b/hutool-json/src/main/java/org/dromara/hutool/json/serializer/JSONMapper.java index 8f24d9be3..abe0ee167 100644 --- a/hutool-json/src/main/java/org/dromara/hutool/json/serializer/JSONMapper.java +++ b/hutool-json/src/main/java/org/dromara/hutool/json/serializer/JSONMapper.java @@ -20,6 +20,7 @@ import org.dromara.hutool.core.lang.Opt; import org.dromara.hutool.core.reflect.TypeReference; import org.dromara.hutool.core.util.ObjUtil; import org.dromara.hutool.json.*; +import org.dromara.hutool.json.serializer.impl.CharSequenceTypeAdapter; import org.dromara.hutool.json.serializer.impl.DefaultDeserializer; import java.io.Serializable; @@ -29,7 +30,7 @@ import java.util.Optional; /** * 对象和JSON值映射器,用于Java对象和JSON对象互转
    *
      - *
    • Java对象转JSON:{@link #toJSON(Object)}
    • + *
    • Java对象转JSON:{@link #toJSON(Object, boolean)}
    • *
    • JSON转Java对象:{@link #toBean(JSON, Type)}
    • *
    *

    @@ -139,22 +140,23 @@ public class JSONMapper implements Serializable { } // region ----- toJSON - /** * 在需要的时候转换映射对象
    * 包装包括: *

      - *
    • array or collection =》 JSONArray
    • - *
    • map =》 JSONObject
    • - *
    • standard property (Double, String, et al) =》 原对象
    • + *
    • array or collection =》 {@link JSONArray}
    • + *
    • map or bean =》 {@link JSONObject}
    • + *
    • standard property (number boolean or char) =》 {@link JSONPrimitive}
    • + *
    • String =》 parseIfString为{@code true}时解析为{@link JSON},{@code false}直接包装为{@link JSONPrimitive}
    • *
    • 其它 =》 尝试包装为JSONObject,否则返回{@code null}
    • *
    * * @param obj 被映射的对象 + * @param parseIfString 如果提供的是字符串,是否解析为JSON,{@code false}则直接包装为{@link JSONPrimitive} * @return 映射后的值,null表示此值需被忽略 */ - public JSON toJSON(final Object obj) { - return mapTo(obj, null); + public JSON toJSON(final Object obj, final boolean parseIfString) { + return mapTo(obj, null, parseIfString); } /** @@ -169,7 +171,7 @@ public class JSONMapper implements Serializable { * @return 映射后的值,null表示此值需被忽略 */ public JSONObject toJSONObject(final Object obj) { - return mapTo(obj, factory.ofObj()); + return mapTo(obj, factory.ofObj(), false); } /** @@ -183,7 +185,7 @@ public class JSONMapper implements Serializable { * @return 映射后的值,null表示此值需被忽略 */ public JSONArray toJSONArray(final Object obj) { - return mapTo(obj, factory.ofArray()); + return mapTo(obj, factory.ofArray(), false); } // endregion @@ -191,19 +193,22 @@ public class JSONMapper implements Serializable { * 在需要的时候转换映射对象
    * 包装包括: *
      - *
    • array or collection =》 JSONArray
    • - *
    • map =》 JSONObject
    • - *
    • standard property (Double, String, et al) =》 原对象
    • + *
    • array or collection =》 {@link JSONArray}
    • + *
    • map or bean =》 {@link JSONObject}
    • + *
    • standard property (number boolean or char) =》 {@link JSONPrimitive}
    • + *
    • String =》 parseIfString为{@code true}时解析为{@link JSON},{@code false}直接包装为{@link JSONPrimitive}
    • *
    • 其它 =》 尝试包装为JSONObject,否则返回{@code null}
    • *
    * - * @param obj 被映射的对象 - * @param json 被映射的到的对象,{@code null}表示根据序列化器自动识别 - * @param JSON类型 + * @param obj 被映射的对象 + * @param json 被映射的到的对象,{@code null}表示根据序列化器自动识别 + * @param parseIfString 如果提供的是字符串,是否解析为JSON,{@code false}则直接包装为{@link JSONPrimitive}, + * 只有json参数为{@code null}时有效 + * @param JSON类型 * @return 映射后的值,null表示此值需被忽略 */ @SuppressWarnings({"unchecked"}) - private T mapTo(Object obj, final T json) { + private T mapTo(Object obj, final T json, final boolean parseIfString) { if (null == obj) { return null; } @@ -224,9 +229,19 @@ public class JSONMapper implements Serializable { // 考虑性能问题,默认原始类型对象直接包装为JSONPrimitive,不再查找TypeAdapter // 如果原始类型想转为其他JSON类型,依旧可以查找TypeAdapter if (JSONPrimitive.isTypeForJSONPrimitive(obj)) { - if (null == json || json instanceof JSONPrimitive) { + if (null == json) { + // 未指定转换的JSON类型,对于String产生二义性 + // 通过parseIfString参数决定是解析字符串还是直接转为原始类型 + if (parseIfString && obj instanceof String) { + return (T) CharSequenceTypeAdapter.INSTANCE.serialize((String)obj, + new SimpleJSONContext(null, factory)); + } return (T) factory.ofPrimitive(obj); } + if (json instanceof JSONPrimitive) { + return (T) factory.ofPrimitive(obj); + } + // 用户想将Primitive对象转为特定JSON对象,则需要查找TypeAdapter } // JSON对象如果与预期结果类型一致,则直接返回 diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/CharSequenceTypeAdapter.java b/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/CharSequenceTypeAdapter.java index 388954958..35035a689 100644 --- a/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/CharSequenceTypeAdapter.java +++ b/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/CharSequenceTypeAdapter.java @@ -19,7 +19,6 @@ package org.dromara.hutool.json.serializer.impl; import org.dromara.hutool.core.reflect.TypeUtil; import org.dromara.hutool.core.text.StrUtil; import org.dromara.hutool.json.JSON; -import org.dromara.hutool.json.JSONFactory; import org.dromara.hutool.json.JSONObject; import org.dromara.hutool.json.JSONPrimitive; import org.dromara.hutool.json.reader.JSONTokener; @@ -86,7 +85,7 @@ public class CharSequenceTypeAdapter implements MatcherJSONSerializer