From a4f0f46ab1d9fbf153bec4d05ff1c908a9111556 Mon Sep 17 00:00:00 2001 From: Looly Date: Wed, 22 Jun 2022 22:18:24 +0800 Subject: [PATCH] fix code --- .../java/cn/hutool/json/InternalJSONUtil.java | 84 +++++- .../main/java/cn/hutool/json/JSONArray.java | 23 +- .../java/cn/hutool/json/JSONBeanParser.java | 19 -- .../main/java/cn/hutool/json/JSONObject.java | 6 +- .../main/java/cn/hutool/json/JSONSupport.java | 1 + .../main/java/cn/hutool/json/JSONUtil.java | 111 +------- .../java/cn/hutool/json/ObjectMapper.java | 267 ------------------ .../cn/hutool/json/mapper/ArrayMapper.java | 145 ++++++++++ .../cn/hutool/json/mapper/ObjectMapper.java | 181 ++++++++++++ .../cn/hutool/json/mapper/package-info.java | 7 + .../hutool/json/serialize/DateJSONString.java | 74 +++++ .../serialize/GlobalSerializeMapping.java | 50 ++-- .../json/serialize/JSONDeserializer.java | 2 +- .../json/{ => serialize}/JSONString.java | 2 +- .../cn/hutool/json/serialize/JSONWriter.java | 16 +- .../serialize/TemporalAccessorSerializer.java | 2 +- .../cn/hutool/json/CustomSerializeTest.java | 17 +- .../java/cn/hutool/json/Issue2090Test.java | 4 +- .../java/cn/hutool/json/IssuesI44E4HTest.java | 2 +- .../java/cn/hutool/json/JSONObjectTest.java | 19 +- .../java/cn/hutool/json/JSONUtilTest.java | 10 +- 21 files changed, 571 insertions(+), 471 deletions(-) delete mode 100755 hutool-json/src/main/java/cn/hutool/json/JSONBeanParser.java delete mode 100755 hutool-json/src/main/java/cn/hutool/json/ObjectMapper.java create mode 100644 hutool-json/src/main/java/cn/hutool/json/mapper/ArrayMapper.java create mode 100644 hutool-json/src/main/java/cn/hutool/json/mapper/ObjectMapper.java create mode 100644 hutool-json/src/main/java/cn/hutool/json/mapper/package-info.java create mode 100644 hutool-json/src/main/java/cn/hutool/json/serialize/DateJSONString.java rename hutool-json/src/main/java/cn/hutool/json/{ => serialize}/JSONString.java (91%) 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 3abf398e7..317feb5e6 100755 --- a/hutool-json/src/main/java/cn/hutool/json/InternalJSONUtil.java +++ b/hutool-json/src/main/java/cn/hutool/json/InternalJSONUtil.java @@ -6,14 +6,20 @@ import cn.hutool.core.lang.mutable.MutableEntry; 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.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.JSONString; 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; @@ -30,17 +36,73 @@ public final class InternalJSONUtil { } /** - * 如果对象是Number 且是 NaN or infinite,将抛出异常 + * 在需要的时候包装对象
+ * 包装包括: + * * - * @param obj 被检查的对象 - * @return 检测后的值 - * @throws JSONException If o is a non-finite number. + * @param object 被包装的对象 + * @param jsonConfig JSON选项 + * @return 包装后的值,null表示此值需被忽略 */ - static Object testValidity(final Object obj) throws JSONException { - if (false == ObjUtil.isValidIfNumber(obj)) { - throw new JSONException("JSON does not allow non-finite numbers."); + static Object wrap(final Object object, final JSONConfig jsonConfig) { + if (object == null) { + return null; + } + if (object instanceof JSON // + || object instanceof JSONString // + || object instanceof CharSequence // + || object instanceof Number // + || ObjUtil.isBasicType(object) // + ) { + if (false == 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 + ) { + 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 obj; } /** @@ -132,7 +194,7 @@ public final class InternalJSONUtil { /** * 将Property的键转化为JSON形式
- * 用于识别类似于:com.luxiaolei.package.hutool这类用点隔开的键
+ * 用于识别类似于:cn.hutool.json这类用点隔开的键
* 注意:不允许重复键 * * @param jsonObject JSONObject @@ -141,7 +203,7 @@ public final class InternalJSONUtil { * @param predicate 属性过滤器,{@link Predicate#test(Object)}为{@code true}保留 * @return JSONObject */ - static JSONObject propertyPut(final JSONObject jsonObject, final Object key, final Object value, final Predicate> predicate) { + public static JSONObject propertyPut(final JSONObject jsonObject, final Object key, final Object value, final Predicate> predicate) { final String[] path = StrUtil.splitToArray(Convert.toStr(key), CharUtil.DOT); final int last = path.length - 1; JSONObject target = jsonObject; @@ -184,7 +246,7 @@ public final class InternalJSONUtil { * @return {@link CopyOptions} * @since 5.8.0 */ - static CopyOptions toCopyOptions(final JSONConfig config) { + public static CopyOptions toCopyOptions(final JSONConfig config) { return CopyOptions.of() .setIgnoreCase(config.isIgnoreCase()) .setIgnoreError(config.isIgnoreError()) diff --git a/hutool-json/src/main/java/cn/hutool/json/JSONArray.java b/hutool-json/src/main/java/cn/hutool/json/JSONArray.java index a3adef91c..ef7bc5d33 100755 --- a/hutool-json/src/main/java/cn/hutool/json/JSONArray.java +++ b/hutool-json/src/main/java/cn/hutool/json/JSONArray.java @@ -8,6 +8,7 @@ import cn.hutool.core.lang.mutable.MutableEntry; import cn.hutool.core.lang.mutable.MutableObj; import cn.hutool.core.text.StrJoiner; import cn.hutool.core.util.ObjUtil; +import cn.hutool.json.mapper.ArrayMapper; import cn.hutool.json.serialize.JSONWriter; import java.io.StringWriter; @@ -148,7 +149,7 @@ public class JSONArray implements JSON, JSONGetter, List, Rando */ public JSONArray(final Object object, final JSONConfig jsonConfig, final Predicate> predicate) throws JSONException { this(DEFAULT_CAPACITY, jsonConfig); - ObjectMapper.of(object).map(this, predicate); + ArrayMapper.of(object, predicate).mapTo(this); } // endregion @@ -328,7 +329,18 @@ public class JSONArray implements JSON, JSONGetter, List, Rando @Override public boolean add(final Object e) { - return addRaw(JSONUtil.wrap(e, this.config), null); + return add(e, null); + } + + /** + * 增加元素 + * + * @param e 元素对象,自动根据对象类型转换为JSON中的对象 + * @param predicate 键值对过滤编辑器,可以通过实现此接口,完成解析前对值的过滤和修改操作,{@code null}表示不过滤,{@link Predicate#test(Object)}为{@code true}保留 + * @return 是否加入成功 + */ + public boolean add(final Object e, final Predicate> predicate) { + return addRaw(InternalJSONUtil.wrap(e, this.config), predicate); } @Override @@ -369,7 +381,7 @@ public class JSONArray implements JSON, JSONGetter, List, Rando continue; } this.add(index); - list.add(JSONUtil.wrap(object, this.config)); + list.add(InternalJSONUtil.wrap(object, this.config)); } return rawList.addAll(index, list); } @@ -429,7 +441,7 @@ public class JSONArray implements JSON, JSONGetter, List, Rando if (null == element && config.isIgnoreNullValue()) { return null; } - return this.rawList.set(index, JSONUtil.wrap(element, this.config)); + return this.rawList.set(index, InternalJSONUtil.wrap(element, this.config)); } @Override @@ -441,8 +453,7 @@ public class JSONArray implements JSON, JSONGetter, List, Rando if (index < 0) { index = 0; } - InternalJSONUtil.testValidity(element); - this.rawList.add(index, JSONUtil.wrap(element, this.config)); + this.rawList.add(index, InternalJSONUtil.wrap(element, this.config)); } else { if (false == config.isIgnoreNullValue()) { while (index != this.size()) { diff --git a/hutool-json/src/main/java/cn/hutool/json/JSONBeanParser.java b/hutool-json/src/main/java/cn/hutool/json/JSONBeanParser.java deleted file mode 100755 index d7cb71125..000000000 --- a/hutool-json/src/main/java/cn/hutool/json/JSONBeanParser.java +++ /dev/null @@ -1,19 +0,0 @@ -package cn.hutool.json; - -/** - * 实现此接口的类可以通过实现{@code parse(value)}方法来将JSON中的值解析为此对象的值
- * 此接口主要用于可以自定义的Bean对象,而无需全局配置转换器。 - * - * @author Looly - * @since 5.7.8 - */ -public interface JSONBeanParser { - - /** - * value转Bean
- * 通过实现此接口,将JSON中的值填充到当前对象的字段值中,即对象自行实现JSON反序列化逻辑 - * - * @param value 被解析的对象类型,可能为JSON或者普通String、Number等 - */ - void parse(T value); -} diff --git a/hutool-json/src/main/java/cn/hutool/json/JSONObject.java b/hutool-json/src/main/java/cn/hutool/json/JSONObject.java index 74e399b30..885f4e543 100755 --- a/hutool-json/src/main/java/cn/hutool/json/JSONObject.java +++ b/hutool-json/src/main/java/cn/hutool/json/JSONObject.java @@ -5,6 +5,7 @@ import cn.hutool.core.lang.mutable.MutableEntry; import cn.hutool.core.map.MapUtil; import cn.hutool.core.map.MapWrapper; import cn.hutool.core.util.ObjUtil; +import cn.hutool.json.mapper.ObjectMapper; import cn.hutool.json.serialize.JSONWriter; import java.io.StringWriter; @@ -127,7 +128,7 @@ public class JSONObject extends MapWrapper implements JSON, JSON */ public JSONObject(final Object source, final JSONConfig config, final Predicate> predicate) { this(DEFAULT_CAPACITY, config); - ObjectMapper.of(source).map(this, predicate); + ObjectMapper.of(source, predicate).mapTo(this); } // -------------------------------------------------------------------------------------------------------------------- Constructor end @@ -279,7 +280,6 @@ public class JSONObject extends MapWrapper implements JSON, JSON * @throws JSONException 如果给定键为{@code null}或者键对应的值存在且为非JSONArray */ public JSONObject append(final String key, final Object value) throws JSONException { - InternalJSONUtil.testValidity(value); final Object object = this.getObj(key); if (object == null) { this.set(key, value); @@ -426,6 +426,6 @@ public class JSONObject extends MapWrapper implements JSON, JSON } else if (checkDuplicate && containsKey(key)) { throw new JSONException("Duplicate key \"{}\"", key); } - return super.put(key, JSONUtil.wrap(InternalJSONUtil.testValidity(value), this.config)); + return super.put(key, InternalJSONUtil.wrap(value, this.config)); } } diff --git a/hutool-json/src/main/java/cn/hutool/json/JSONSupport.java b/hutool-json/src/main/java/cn/hutool/json/JSONSupport.java index 98a1f182a..599e07b44 100644 --- a/hutool-json/src/main/java/cn/hutool/json/JSONSupport.java +++ b/hutool-json/src/main/java/cn/hutool/json/JSONSupport.java @@ -2,6 +2,7 @@ package cn.hutool.json; import cn.hutool.core.bean.BeanUtil; import cn.hutool.json.serialize.JSONDeserializer; +import cn.hutool.json.serialize.JSONString; /** * JSON支持
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 13ca0cba2..763084a14 100755 --- a/hutool-json/src/main/java/cn/hutool/json/JSONUtil.java +++ b/hutool-json/src/main/java/cn/hutool/json/JSONUtil.java @@ -4,9 +4,7 @@ import cn.hutool.core.codec.HexUtil; import cn.hutool.core.io.IORuntimeException; import cn.hutool.core.io.file.FileReader; import cn.hutool.core.map.MapWrapper; -import cn.hutool.core.reflect.ClassUtil; 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.core.util.ObjUtil; @@ -14,7 +12,6 @@ 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.serialize.JSONSerializer; import java.io.File; import java.io.IOException; @@ -22,13 +19,8 @@ import java.io.StringWriter; import java.io.Writer; import java.lang.reflect.Type; import java.nio.charset.Charset; -import java.sql.SQLException; -import java.time.temporal.TemporalAccessor; -import java.util.Calendar; -import java.util.Date; import java.util.Iterator; import java.util.List; -import java.util.Map; /** * JSON工具类 @@ -334,7 +326,7 @@ public class JSONUtil { return StrUtil.str((CharSequence) obj); } - if(obj instanceof Number){ + if (obj instanceof Number) { return obj.toString(); } return toJsonStr(parse(obj, jsonConfig)); @@ -684,87 +676,6 @@ public class JSONUtil { return builder.toString(); } - /** - * 在需要的时候包装对象
- * 包装包括: - *
    - *
  • array or collection =》 JSONArray
  • - *
  • map =》 JSONObject
  • - *
  • standard property (Double, String, et al) =》 原对象
  • - *
  • 来自于java包 =》 字符串
  • - *
  • 其它 =》 尝试包装为JSONObject,否则返回{@code null}
  • - *
- * - * @param object 被包装的对象 - * @param jsonConfig JSON选项 - * @return 包装后的值,null表示此值需被忽略 - */ - @SuppressWarnings({"rawtypes", "unchecked"}) - public static Object wrap(final Object object, final JSONConfig jsonConfig) { - if (object == null) { - return null; - } - if (object instanceof JSON // - || object instanceof JSONString // - || object instanceof CharSequence // - || object instanceof Number // - || ObjUtil.isBasicType(object) // - ) { - return object; - } - - // 自定义序列化 - final JSONSerializer serializer = GlobalSerializeMapping.getSerializer(object.getClass()); - if (null != serializer) { - final Type jsonType = TypeUtil.getTypeArgument(serializer.getClass()); - if (null != jsonType) { - if (serializer instanceof JSONObjectSerializer) { - serializer.serialize(new JSONObject(jsonConfig), object); - } else if (serializer instanceof JSONArraySerializer) { - serializer.serialize(new JSONArray(jsonConfig), 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 - ) { - 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; - } - } - /** * 格式化JSON字符串,此方法并不严格检查JSON的格式正确与否 * @@ -832,11 +743,11 @@ public class JSONUtil { * * @param type 对象类型 * @param serializer 序列化器实现 - * @see GlobalSerializeMapping#put(Type, JSONArraySerializer) - * @since 4.6.5 + * @see GlobalSerializeMapping#putSerializer(Type, JSONObjectSerializer) + * @since 6.0.0 */ - public static void putSerializer(final Type type, final JSONArraySerializer serializer) { - GlobalSerializeMapping.put(type, serializer); + public static void putSerializer(final Type type, final JSONObjectSerializer serializer) { + GlobalSerializeMapping.putSerializer(type, serializer); } /** @@ -844,11 +755,11 @@ public class JSONUtil { * * @param type 对象类型 * @param serializer 序列化器实现 - * @see GlobalSerializeMapping#put(Type, JSONObjectSerializer) - * @since 4.6.5 + * @see GlobalSerializeMapping#putSerializer(Type, JSONArraySerializer) + * @since 6.0.0 */ - public static void putSerializer(final Type type, final JSONObjectSerializer serializer) { - GlobalSerializeMapping.put(type, serializer); + public static void putSerializer(final Type type, final JSONArraySerializer serializer) { + GlobalSerializeMapping.putSerializer(type, serializer); } /** @@ -856,11 +767,11 @@ public class JSONUtil { * * @param type 对象类型 * @param deserializer 反序列化器实现 - * @see GlobalSerializeMapping#put(Type, JSONDeserializer) + * @see GlobalSerializeMapping#putDeserializer(Type, JSONDeserializer) * @since 4.6.5 */ public static void putDeserializer(final Type type, final JSONDeserializer deserializer) { - GlobalSerializeMapping.put(type, deserializer); + GlobalSerializeMapping.putDeserializer(type, deserializer); } // --------------------------------------------------------------------------------------------- Private method start diff --git a/hutool-json/src/main/java/cn/hutool/json/ObjectMapper.java b/hutool-json/src/main/java/cn/hutool/json/ObjectMapper.java deleted file mode 100755 index 10e5f1baf..000000000 --- a/hutool-json/src/main/java/cn/hutool/json/ObjectMapper.java +++ /dev/null @@ -1,267 +0,0 @@ -package cn.hutool.json; - -import cn.hutool.core.bean.BeanUtil; -import cn.hutool.core.bean.copier.CopyOptions; -import cn.hutool.core.collection.iter.ArrayIter; -import cn.hutool.core.convert.Convert; -import cn.hutool.core.io.IoUtil; -import cn.hutool.core.lang.mutable.Mutable; -import cn.hutool.core.lang.mutable.MutableEntry; -import cn.hutool.core.reflect.TypeUtil; -import cn.hutool.core.text.StrUtil; -import cn.hutool.core.util.ArrayUtil; -import cn.hutool.json.serialize.GlobalSerializeMapping; -import cn.hutool.json.serialize.JSONObjectSerializer; -import cn.hutool.json.serialize.JSONSerializer; - -import java.io.InputStream; -import java.io.Reader; -import java.util.Enumeration; -import java.util.Iterator; -import java.util.Map; -import java.util.ResourceBundle; -import java.util.function.Predicate; - -/** - * 对象和JSON映射器,用于转换对象为JSON,支持: - *
    - *
  • Map 转 JSONObject,将键值对加入JSON对象
  • - *
  • Map.Entry 转 JSONObject
  • - *
  • CharSequence 转 JSONObject,使用JSONTokener解析
  • - *
  • {@link Reader} 转 JSONObject,使用JSONTokener解析
  • - *
  • {@link InputStream} 转 JSONObject,使用JSONTokener解析
  • - *
  • JSONTokener 转 JSONObject,直接解析
  • - *
  • ResourceBundle 转 JSONObject
  • - *
  • Bean 转 JSONObject,调用其getters方法(getXXX或者isXXX)获得值,加入到JSON对象。例如:如果JavaBean对象中有个方法getName(),值为"张三",获得的键值对为:name: "张三"
  • - *
- * - * @author looly - * @since 5.8.0 - */ -public class ObjectMapper { - - /** - * 创建ObjectMapper - * - * @param source 来源对象 - * @return ObjectMapper - */ - public static ObjectMapper of(final Object source) { - return new ObjectMapper(source); - } - - private final Object source; - - /** - * 构造 - * - * @param source 来源对象 - */ - public ObjectMapper(final Object source) { - this.source = source; - } - - /** - * 将给定对象转换为{@link JSONObject} - * - * @param jsonObject 目标{@link JSONObject} - * @param predicate 键值对过滤编辑器,可以通过实现此接口,完成解析前对键值对的过滤和修改操作,{@link Predicate#test(Object)}为{@code true}保留 - */ - @SuppressWarnings({"rawtypes", "unchecked"}) - public void map(final JSONObject jsonObject, final Predicate> predicate) { - final Object source = this.source; - if (null == source) { - return; - } - - // 自定义序列化 - final JSONSerializer serializer = GlobalSerializeMapping.getSerializer(source.getClass()); - if (serializer instanceof JSONObjectSerializer) { - serializer.serialize(jsonObject, source); - return; - } - - if (source instanceof JSONArray) { - // 不支持集合类型转换为JSONObject - throw new JSONException("Unsupported type [{}] to JSONObject!", source.getClass()); - } - - if (source instanceof Map) { - // Map - for (final Map.Entry e : ((Map) source).entrySet()) { - jsonObject.set(Convert.toStr(e.getKey()), e.getValue(), predicate, false); - } - } else if (source instanceof Map.Entry) { - final Map.Entry entry = (Map.Entry) source; - jsonObject.set(Convert.toStr(entry.getKey()), entry.getValue(), predicate, false); - } else if (source instanceof CharSequence) { - // 可能为JSON字符串 - mapFromStr((CharSequence) source, jsonObject, predicate); - } else if (source instanceof Reader) { - mapFromTokener(new JSONTokener((Reader) source, jsonObject.getConfig()), jsonObject, predicate); - } else if (source instanceof InputStream) { - mapFromTokener(new JSONTokener((InputStream) source, jsonObject.getConfig()), jsonObject, predicate); - } else if (source instanceof byte[]) { - mapFromTokener(new JSONTokener(IoUtil.toStream((byte[]) source), jsonObject.getConfig()), jsonObject, predicate); - } else if (source instanceof JSONTokener) { - // JSONTokener - mapFromTokener((JSONTokener) source, jsonObject, predicate); - } else if (source instanceof ResourceBundle) { - // JSONTokener - mapFromResourceBundle((ResourceBundle) source, jsonObject, predicate); - } else if (BeanUtil.isReadableBean(source.getClass())) { - // 普通Bean - mapFromBean(source, jsonObject, predicate); - } else { - // 不支持对象类型转换为JSONObject - throw new JSONException("Unsupported type [{}] to JSONObject!", source.getClass()); - } - } - - /** - * 将给定对象转换为{@link JSONArray} - * - * @param jsonArray 目标{@link JSONArray} - * @param predicate 键值对过滤编辑器,可以通过实现此接口,完成解析前对值的过滤和修改操作,{@code null}表示不过滤,{@link Predicate#test(Object)}为{@code true}保留 - * @throws JSONException 非数组或集合 - */ - @SuppressWarnings({"rawtypes", "unchecked"}) - public void map(final JSONArray jsonArray, final Predicate> predicate) throws JSONException { - final Object source = this.source; - if (null == source) { - return; - } - - final JSONSerializer serializer = GlobalSerializeMapping.getSerializer(source.getClass()); - if (null != serializer && JSONArray.class.equals(TypeUtil.getTypeArgument(serializer.getClass()))) { - // 自定义序列化 - serializer.serialize(jsonArray, source); - } else if (source instanceof CharSequence) { - // JSON字符串 - mapFromStr((CharSequence) source, jsonArray, predicate); - } else if (source instanceof Reader) { - mapFromTokener(new JSONTokener((Reader) source, jsonArray.getConfig()), jsonArray, predicate); - } else if (source instanceof InputStream) { - mapFromTokener(new JSONTokener((InputStream) source, jsonArray.getConfig()), jsonArray, predicate); - } else if (source instanceof byte[]) { - final byte[] bytesSource = (byte[]) source; - if ('[' == bytesSource[0] && ']' == bytesSource[bytesSource.length - 1]) { - mapFromTokener(new JSONTokener(IoUtil.toStream(bytesSource), jsonArray.getConfig()), jsonArray, predicate); - } else { - // https://github.com/dromara/hutool/issues/2369 - // 非标准的二进制流,则按照普通数组对待 - for (final byte b : bytesSource) { - jsonArray.add(b); - } - } - } else if (source instanceof JSONTokener) { - mapFromTokener((JSONTokener) source, jsonArray, predicate); - } else { - final Iterator iter; - if (ArrayUtil.isArray(source)) {// 数组 - iter = new ArrayIter<>(source); - } else if (source instanceof Iterator) {// Iterator - iter = ((Iterator) source); - } else if (source instanceof Iterable) {// Iterable - iter = ((Iterable) source).iterator(); - } else { - throw new JSONException("JSONArray initial value should be a string or collection or array."); - } - - final JSONConfig config = jsonArray.getConfig(); - Object next; - while (iter.hasNext()) { - next = iter.next(); - // 检查循环引用 - if (next != source) { - jsonArray.addRaw(JSONUtil.wrap(next, config), predicate); - } - } - } - } - - /** - * 从{@link ResourceBundle}转换 - * - * @param bundle ResourceBundle - * @param jsonObject {@link JSONObject} - * @param predicate 键值对过滤编辑器,可以通过实现此接口,完成解析前对键值对的过滤和修改操作,{@code null}表示不过滤,{@link Predicate#test(Object)}为{@code true}保留 - * @since 5.3.1 - */ - private static void mapFromResourceBundle(final ResourceBundle bundle, final JSONObject jsonObject, final Predicate> predicate) { - final Enumeration keys = bundle.getKeys(); - while (keys.hasMoreElements()) { - final String key = keys.nextElement(); - if (key != null) { - InternalJSONUtil.propertyPut(jsonObject, key, bundle.getString(key), predicate); - } - } - } - - /** - * 从字符串转换 - * - * @param source JSON字符串 - * @param jsonObject {@link JSONObject} - * @param predicate 键值对过滤编辑器,可以通过实现此接口,完成解析前对键值对的过滤和修改操作,{@code null}表示不过滤,{@link Predicate#test(Object)}为{@code true}保留 - */ - private static void mapFromStr(final CharSequence source, final JSONObject jsonObject, final Predicate> predicate) { - final String jsonStr = StrUtil.trim(source); - if (StrUtil.startWith(jsonStr, '<')) { - // 可能为XML - XML.toJSONObject(jsonObject, jsonStr, false); - return; - } - mapFromTokener(new JSONTokener(StrUtil.trim(source), jsonObject.getConfig()), jsonObject, predicate); - } - - /** - * 初始化 - * - * @param source JSON字符串 - * @param jsonArray {@link JSONArray} - * @param predicate 值过滤编辑器,可以通过实现此接口,完成解析前对值的过滤和修改操作,{@code null}表示不过滤,,{@link Predicate#test(Object)}为{@code true}保留 - */ - private void mapFromStr(final CharSequence source, final JSONArray jsonArray, final Predicate> predicate) { - if (null != source) { - mapFromTokener(new JSONTokener(StrUtil.trim(source), jsonArray.getConfig()), jsonArray, predicate); - } - } - - /** - * 从{@link JSONTokener}转换 - * - * @param x JSONTokener - * @param jsonObject {@link JSONObject} - * @param predicate 键值对过滤编辑器,可以通过实现此接口,完成解析前对键值对的过滤和修改操作,{@link Predicate#test(Object)}为{@code true}保留 - */ - private static void mapFromTokener(final JSONTokener x, final JSONObject jsonObject, final Predicate> predicate) { - JSONParser.of(x).parseTo(jsonObject, predicate); - } - - /** - * 初始化 - * - * @param x {@link JSONTokener} - * @param jsonArray {@link JSONArray} - * @param predicate 值过滤编辑器,可以通过实现此接口,完成解析前对值的过滤和修改操作,{@code null}表示不过滤,{@link Predicate#test(Object)}为{@code true}保留 - */ - private static void mapFromTokener(final JSONTokener x, final JSONArray jsonArray, final Predicate> predicate) { - JSONParser.of(x).parseTo(jsonArray, predicate); - } - - /** - * 从Bean转换 - * - * @param bean Bean对象 - * @param predicate 过滤器,,{@link Predicate#test(Object)}为{@code true}保留,null表示保留。 - * @param jsonObject {@link JSONObject} - */ - private static void mapFromBean(final Object bean, final JSONObject jsonObject, final Predicate> predicate) { - final CopyOptions copyOptions = InternalJSONUtil.toCopyOptions(jsonObject.getConfig()); - if (null != predicate) { - copyOptions.setFieldEditor((entry -> predicate.test(entry) ? entry : null)); - } - BeanUtil.beanToMap(bean, jsonObject, copyOptions); - } -} diff --git a/hutool-json/src/main/java/cn/hutool/json/mapper/ArrayMapper.java b/hutool-json/src/main/java/cn/hutool/json/mapper/ArrayMapper.java new file mode 100644 index 000000000..c4dd10ec0 --- /dev/null +++ b/hutool-json/src/main/java/cn/hutool/json/mapper/ArrayMapper.java @@ -0,0 +1,145 @@ +package cn.hutool.json.mapper; + +import cn.hutool.core.collection.iter.ArrayIter; +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.lang.mutable.Mutable; +import cn.hutool.core.text.StrUtil; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONException; +import cn.hutool.json.JSONParser; +import cn.hutool.json.JSONTokener; +import cn.hutool.json.serialize.GlobalSerializeMapping; +import cn.hutool.json.serialize.JSONSerializer; + +import java.io.InputStream; +import java.io.Reader; +import java.util.Iterator; +import java.util.function.Predicate; + +/** + * 对象和JSONArray映射器,用于转换对象为JSONArray,支持: + *
    + *
  • CharSequence 转 JSONArray,使用JSONTokener解析
  • + *
  • {@link Reader} 转 JSONArray,使用JSONTokener解析
  • + *
  • {@link InputStream} 转 JSONArray,使用JSONTokener解析
  • + *
  • JSONTokener 转 JSONArray,直接解析
  • + *
  • Iterable 转 JSONArray
  • + *
  • Iterator 转 JSONArray
  • + *
  • 数组 转 JSONArray
  • + *
+ * + * @author looly + * @since 6.0.0 + */ +public class ArrayMapper { + /** + * 创建ArrayMapper + * + * @param source 来源对象 + * @param predicate 值过滤编辑器,可以通过实现此接口,完成解析前对值的过滤和修改操作,{@code null}表示不过滤,,{@link Predicate#test(Object)}为{@code true}保留 + * @return ObjectMapper + */ + public static ArrayMapper of(final Object source, final Predicate> predicate) { + return new ArrayMapper(source, predicate); + } + + private final Object source; + private final Predicate> predicate; + + /** + * 构造 + * + * @param source 来源对象 + * @param predicate 值过滤编辑器,可以通过实现此接口,完成解析前对值的过滤和修改操作,{@code null}表示不过滤,,{@link Predicate#test(Object)}为{@code true}保留 + */ + public ArrayMapper(final Object source, final Predicate> predicate) { + this.source = source; + this.predicate = predicate; + } + + /** + * 将给定对象转换为{@link JSONArray} + * + * @param jsonArray 目标{@link JSONArray} + * @throws JSONException 非数组或集合 + */ + @SuppressWarnings({"rawtypes", "unchecked"}) + public void mapTo(final JSONArray jsonArray) throws JSONException { + final Object source = this.source; + if (null == source) { + return; + } + + // 自定义序列化 + final JSONSerializer serializer = GlobalSerializeMapping.getSerializer(source.getClass()); + if (null != serializer) { + serializer.serialize(jsonArray, source); + return; + } + + if (source instanceof CharSequence) { + // JSON字符串 + mapFromStr((CharSequence) source, jsonArray); + } else if (source instanceof Reader) { + mapFromTokener(new JSONTokener((Reader) source, jsonArray.getConfig()), jsonArray); + } else if (source instanceof InputStream) { + mapFromTokener(new JSONTokener((InputStream) source, jsonArray.getConfig()), jsonArray); + } else if (source instanceof byte[]) { + final byte[] bytesSource = (byte[]) source; + if ('[' == bytesSource[0] && ']' == bytesSource[bytesSource.length - 1]) { + mapFromTokener(new JSONTokener(IoUtil.toStream(bytesSource), jsonArray.getConfig()), jsonArray); + } else { + // https://github.com/dromara/hutool/issues/2369 + // 非标准的二进制流,则按照普通数组对待 + for (final byte b : bytesSource) { + jsonArray.add(b); + } + } + } else if (source instanceof JSONTokener) { + mapFromTokener((JSONTokener) source, jsonArray); + } else { + final Iterator iter; + if (ArrayUtil.isArray(source)) {// 数组 + iter = new ArrayIter<>(source); + } else if (source instanceof Iterator) {// Iterator + iter = ((Iterator) source); + } else if (source instanceof Iterable) {// Iterable + iter = ((Iterable) source).iterator(); + } else { + throw new JSONException("JSONArray initial value should be a string or collection or array."); + } + + Object next; + while (iter.hasNext()) { + next = iter.next(); + // 检查循环引用 + if (next != source) { + jsonArray.add(next, predicate); + } + } + } + } + + /** + * 初始化 + * + * @param source JSON字符串 + * @param jsonArray {@link JSONArray} + */ + private void mapFromStr(final CharSequence source, final JSONArray jsonArray) { + if (null != source) { + mapFromTokener(new JSONTokener(StrUtil.trim(source), jsonArray.getConfig()), jsonArray); + } + } + + /** + * 初始化 + * + * @param x {@link JSONTokener} + * @param jsonArray {@link JSONArray} + */ + private void mapFromTokener(final JSONTokener x, final JSONArray jsonArray) { + JSONParser.of(x).parseTo(jsonArray, this.predicate); + } +} diff --git a/hutool-json/src/main/java/cn/hutool/json/mapper/ObjectMapper.java b/hutool-json/src/main/java/cn/hutool/json/mapper/ObjectMapper.java new file mode 100644 index 000000000..4b0682e1d --- /dev/null +++ b/hutool-json/src/main/java/cn/hutool/json/mapper/ObjectMapper.java @@ -0,0 +1,181 @@ +package cn.hutool.json.mapper; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.bean.copier.CopyOptions; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.lang.mutable.MutableEntry; +import cn.hutool.core.text.StrUtil; +import cn.hutool.json.InternalJSONUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONException; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONParser; +import cn.hutool.json.JSONTokener; +import cn.hutool.json.XML; +import cn.hutool.json.serialize.GlobalSerializeMapping; +import cn.hutool.json.serialize.JSONSerializer; + +import java.io.InputStream; +import java.io.Reader; +import java.util.Enumeration; +import java.util.Map; +import java.util.ResourceBundle; +import java.util.function.Predicate; + +/** + * 对象和JSONObject映射器,用于转换对象为JSONObject,支持: + *
    + *
  • Map 转 JSONObject,将键值对加入JSON对象
  • + *
  • Map.Entry 转 JSONObject
  • + *
  • CharSequence 转 JSONObject,使用JSONTokener解析
  • + *
  • {@link Reader} 转 JSONObject,使用JSONTokener解析
  • + *
  • {@link InputStream} 转 JSONObject,使用JSONTokener解析
  • + *
  • JSONTokener 转 JSONObject,直接解析
  • + *
  • ResourceBundle 转 JSONObject
  • + *
  • Bean 转 JSONObject,调用其getters方法(getXXX或者isXXX)获得值,加入到JSON对象。例如:如果JavaBean对象中有个方法getName(),值为"张三",获得的键值对为:name: "张三"
  • + *
+ * + * @author looly + * @since 5.8.0 + */ +public class ObjectMapper { + + /** + * 创建ObjectMapper + * + * @param source 来源对象 + * @param predicate 键值对过滤编辑器,可以通过实现此接口,完成解析前对键值对的过滤和修改操作,{@link Predicate#test(Object)}为{@code true}保留 + * @return ObjectMapper + */ + public static ObjectMapper of(final Object source, final Predicate> predicate) { + return new ObjectMapper(source, predicate); + } + + private final Object source; + private final Predicate> predicate; + + /** + * 构造 + * + * @param source 来源对象 + * @param predicate 键值对过滤编辑器,可以通过实现此接口,完成解析前对键值对的过滤和修改操作,{@link Predicate#test(Object)}为{@code true}保留 + */ + public ObjectMapper(final Object source, final Predicate> predicate) { + this.source = source; + this.predicate = predicate; + } + + /** + * 将给定对象转换为{@link JSONObject} + * + * @param jsonObject 目标{@link JSONObject} + */ + @SuppressWarnings({"rawtypes", "unchecked"}) + public void mapTo(final JSONObject jsonObject) { + final Object source = this.source; + if (null == source) { + return; + } + + // 自定义序列化 + final JSONSerializer serializer = GlobalSerializeMapping.getSerializer(source.getClass()); + if (null != serializer) { + serializer.serialize(jsonObject, source); + return; + } + + if (source instanceof JSONArray) { + // 不支持集合类型转换为JSONObject + throw new JSONException("Unsupported type [{}] to JSONObject!", source.getClass()); + } + + if (source instanceof Map) { + // Map + for (final Map.Entry e : ((Map) source).entrySet()) { + jsonObject.set(Convert.toStr(e.getKey()), e.getValue(), predicate, false); + } + } else if (source instanceof Map.Entry) { + final Map.Entry entry = (Map.Entry) source; + jsonObject.set(Convert.toStr(entry.getKey()), entry.getValue(), predicate, false); + } else if (source instanceof CharSequence) { + // 可能为JSON字符串 + mapFromStr((CharSequence) source, jsonObject); + } else if (source instanceof Reader) { + mapFromTokener(new JSONTokener((Reader) source, jsonObject.getConfig()), jsonObject); + } else if (source instanceof InputStream) { + mapFromTokener(new JSONTokener((InputStream) source, jsonObject.getConfig()), jsonObject); + } else if (source instanceof byte[]) { + mapFromTokener(new JSONTokener(IoUtil.toStream((byte[]) source), jsonObject.getConfig()), jsonObject); + } else if (source instanceof JSONTokener) { + // JSONTokener + mapFromTokener((JSONTokener) source, jsonObject); + } else if (source instanceof ResourceBundle) { + // JSONTokener + mapFromResourceBundle((ResourceBundle) source, jsonObject); + } else if (BeanUtil.isReadableBean(source.getClass())) { + // 普通Bean + mapFromBean(source, jsonObject); + } else { + // 不支持对象类型转换为JSONObject + throw new JSONException("Unsupported type [{}] to JSONObject!", source.getClass()); + } + } + + /** + * 从{@link ResourceBundle}转换 + * + * @param bundle ResourceBundle + * @param jsonObject {@link JSONObject} + * @since 5.3.1 + */ + private void mapFromResourceBundle(final ResourceBundle bundle, final JSONObject jsonObject) { + final Enumeration keys = bundle.getKeys(); + while (keys.hasMoreElements()) { + final String key = keys.nextElement(); + if (key != null) { + InternalJSONUtil.propertyPut(jsonObject, key, bundle.getString(key), this.predicate); + } + } + } + + /** + * 从字符串转换 + * + * @param source JSON字符串 + * @param jsonObject {@link JSONObject} + */ + private void mapFromStr(final CharSequence source, final JSONObject jsonObject) { + final String jsonStr = StrUtil.trim(source); + if (StrUtil.startWith(jsonStr, '<')) { + // 可能为XML + XML.toJSONObject(jsonObject, jsonStr, false); + return; + } + mapFromTokener(new JSONTokener(StrUtil.trim(source), jsonObject.getConfig()), jsonObject); + } + + /** + * 从{@link JSONTokener}转换 + * + * @param x JSONTokener + * @param jsonObject {@link JSONObject} + */ + private void mapFromTokener(final JSONTokener x, final JSONObject jsonObject) { + JSONParser.of(x).parseTo(jsonObject, this.predicate); + } + + /** + * 从Bean转换 + * + * @param bean Bean对象 + * @param jsonObject {@link JSONObject} + */ + private void mapFromBean(final Object bean, final JSONObject jsonObject) { + final CopyOptions copyOptions = InternalJSONUtil.toCopyOptions(jsonObject.getConfig()); + if (null != this.predicate) { + copyOptions.setFieldEditor((entry -> this.predicate.test(entry) ? entry : null)); + } + BeanUtil.beanToMap(bean, jsonObject, copyOptions); + } +} diff --git a/hutool-json/src/main/java/cn/hutool/json/mapper/package-info.java b/hutool-json/src/main/java/cn/hutool/json/mapper/package-info.java new file mode 100644 index 000000000..954bc1513 --- /dev/null +++ b/hutool-json/src/main/java/cn/hutool/json/mapper/package-info.java @@ -0,0 +1,7 @@ +/** + * JSON和对象之间的映射封装 + * + * @author Looly + * @since 6.0.0 + */ +package cn.hutool.json.mapper; diff --git a/hutool-json/src/main/java/cn/hutool/json/serialize/DateJSONString.java b/hutool-json/src/main/java/cn/hutool/json/serialize/DateJSONString.java new file mode 100644 index 000000000..efdad9c4f --- /dev/null +++ b/hutool-json/src/main/java/cn/hutool/json/serialize/DateJSONString.java @@ -0,0 +1,74 @@ +package cn.hutool.json.serialize; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.date.TemporalAccessorUtil; +import cn.hutool.core.date.format.GlobalCustomFormat; +import cn.hutool.core.text.StrUtil; +import cn.hutool.json.JSONConfig; +import cn.hutool.json.JSONUtil; + +import java.time.temporal.TemporalAccessor; +import java.util.Calendar; +import java.util.Date; + +/** + * 自定义的日期表示形式
+ * 支持包括:{@link Date}、{@link Calendar}、{@link TemporalAccessor} + * + * @author looly + * @since 6.0.0 + */ +public class DateJSONString implements JSONString { + + final Object dateObj; + final JSONConfig jsonConfig; + + public DateJSONString(final Object dateObj, final JSONConfig jsonConfig) { + this.dateObj = dateObj; + this.jsonConfig = jsonConfig; + } + + @Override + public String toJSONString() { + return formatDate(this.jsonConfig.getDateFormat()); + } + + /** + * 按照给定格式格式化日期,格式为空时返回时间戳字符串 + * + * @param format 格式 + * @return 日期字符串 + */ + private String formatDate(final String format) { + if (StrUtil.isNotBlank(format)) { + final String dateStr; + if (dateObj instanceof TemporalAccessor) { + dateStr = TemporalAccessorUtil.format((TemporalAccessor) dateObj, format); + } else { + dateStr = DateUtil.format(Convert.toDate(dateObj), format); + } + + if (GlobalCustomFormat.FORMAT_SECONDS.equals(format) + || GlobalCustomFormat.FORMAT_MILLISECONDS.equals(format)) { + // Hutool自定义的秒和毫秒表示,默认不包装双引号 + return dateStr; + } + //用户定义了日期格式 + return JSONUtil.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); + } +} 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 13bd2ed65..7228a1cc7 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 @@ -11,10 +11,10 @@ import java.util.concurrent.ConcurrentHashMap; /** * 全局的序列化和反序列化器映射
- * 在JSON和Java对象转换过程中,优先使用注册于此处的自定义转换 + * 在JSON和Java对象转换过程中,优先使用注册于此处的自定义转换
+ * 分别定义{@link JSONObjectSerializer}和{@link JSONArraySerializer}的原因是,实际加入对象到JSON中时,无法区分是JSONObject还是JSONArray * * @author Looly - * */ public class GlobalSerializeMapping { @@ -39,33 +39,33 @@ public class GlobalSerializeMapping { } /** - * 加入自定义的序列化器 + * 加入自定义的JSONArray序列化器 * - * @param type 对象类型 + * @param type 对象类型 * @param serializer 序列化器实现 */ - public static void put(final Type type, final JSONArraySerializer serializer) { + public static void putSerializer(final Type type, final JSONArraySerializer serializer) { + putInternal(type, serializer); + } + + /** + * 加入自定义的JSONObject序列化器 + * + * @param type 对象类型 + * @param serializer 序列化器实现 + */ + public static void putSerializer(final Type type, final JSONObjectSerializer serializer) { putInternal(type, serializer); } /** * 加入自定义的序列化器 * - * @param type 对象类型 - * @param serializer 序列化器实现 - */ - public static void put(final Type type, final JSONObjectSerializer serializer) { - putInternal(type, serializer); - } - - /** - * 加入自定义的序列化器 - * - * @param type 对象类型 + * @param type 对象类型 * @param serializer 序列化器实现 */ synchronized private static void putInternal(final Type type, final JSONSerializer serializer) { - if(null == serializerMap) { + if (null == serializerMap) { serializerMap = new ConcurrentHashMap<>(); } serializerMap.put(type, serializer); @@ -74,11 +74,11 @@ public class GlobalSerializeMapping { /** * 加入自定义的反序列化器 * - * @param type 对象类型 + * @param type 对象类型 * @param deserializer 反序列化器实现 */ - synchronized public static void put(final Type type, final JSONDeserializer deserializer) { - if(null == deserializerMap) { + synchronized public static void putDeserializer(final Type type, final JSONDeserializer deserializer) { + if (null == deserializerMap) { deserializerMap = new ConcurrentHashMap<>(); } deserializerMap.put(type, deserializer); @@ -86,11 +86,12 @@ public class GlobalSerializeMapping { /** * 获取自定义的序列化器,如果未定义返回{@code null} + * * @param type 类型 * @return 自定义的序列化器或者{@code null} */ - public static JSONSerializer getSerializer(final Type type){ - if(null == serializerMap) { + public static JSONSerializer getSerializer(final Type type) { + if (null == serializerMap) { return null; } return serializerMap.get(type); @@ -98,11 +99,12 @@ public class GlobalSerializeMapping { /** * 获取自定义的反序列化器,如果未定义返回{@code null} + * * @param type 类型 * @return 自定义的反序列化器或者{@code null} */ - public static JSONDeserializer getDeserializer(final Type type){ - if(null == deserializerMap) { + public static JSONDeserializer getDeserializer(final Type type) { + if (null == deserializerMap) { return null; } return deserializerMap.get(type); diff --git a/hutool-json/src/main/java/cn/hutool/json/serialize/JSONDeserializer.java b/hutool-json/src/main/java/cn/hutool/json/serialize/JSONDeserializer.java index f6b2eb859..cdb66e858 100644 --- a/hutool-json/src/main/java/cn/hutool/json/serialize/JSONDeserializer.java +++ b/hutool-json/src/main/java/cn/hutool/json/serialize/JSONDeserializer.java @@ -7,7 +7,7 @@ import java.lang.reflect.Type; /** * JSON自定义反序列化接口,实现JSON to Bean,使用方式为: *
    - *
  • 定义好反序列化规则,使用{@link GlobalSerializeMapping#put(Type, JSONDeserializer)},关联指定类型与转换器实现反序列化。
  • + *
  • 定义好反序列化规则,使用{@link GlobalSerializeMapping#putDeserializer(Type, JSONDeserializer)},关联指定类型与转换器实现反序列化。
  • *
  • 使Bean实现此接口,调用{@link #deserialize(JSON)}解析字段,返回this即可。
  • *
* diff --git a/hutool-json/src/main/java/cn/hutool/json/JSONString.java b/hutool-json/src/main/java/cn/hutool/json/serialize/JSONString.java similarity index 91% rename from hutool-json/src/main/java/cn/hutool/json/JSONString.java rename to hutool-json/src/main/java/cn/hutool/json/serialize/JSONString.java index 05cedf916..f00b9a244 100644 --- a/hutool-json/src/main/java/cn/hutool/json/JSONString.java +++ b/hutool-json/src/main/java/cn/hutool/json/serialize/JSONString.java @@ -1,4 +1,4 @@ -package cn.hutool.json; +package cn.hutool.json.serialize; /** * {@code JSONString}接口定义了一个{@code toJSONString()}
diff --git a/hutool-json/src/main/java/cn/hutool/json/serialize/JSONWriter.java b/hutool-json/src/main/java/cn/hutool/json/serialize/JSONWriter.java index 4bc67fb33..b9df12106 100755 --- a/hutool-json/src/main/java/cn/hutool/json/serialize/JSONWriter.java +++ b/hutool-json/src/main/java/cn/hutool/json/serialize/JSONWriter.java @@ -7,14 +7,10 @@ import cn.hutool.core.date.format.GlobalCustomFormat; import cn.hutool.core.io.IORuntimeException; import cn.hutool.core.math.NumberUtil; import cn.hutool.core.text.StrUtil; -import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.CharUtil; 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.JSONString; import cn.hutool.json.JSONUtil; import java.io.IOException; @@ -22,8 +18,6 @@ import java.io.Writer; import java.time.temporal.TemporalAccessor; import java.util.Calendar; import java.util.Date; -import java.util.Iterator; -import java.util.Map; /** * JSON数据写出器
@@ -234,10 +228,6 @@ public class JSONWriter extends Writer { writeRaw(StrUtil.NULL); } else if (value instanceof JSON) { ((JSON) value).write(writer, indentFactor, indent); - } else if (value instanceof Map || value instanceof Map.Entry) { - new JSONObject(value).write(writer, indentFactor, indent); - } else if (value instanceof Iterable || value instanceof Iterator || ArrayUtil.isArray(value)) { - new JSONArray(value).write(writer, indentFactor, indent); } else if (value instanceof Number) { writeNumberValue((Number) value); } else if (value instanceof Date || value instanceof Calendar || value instanceof TemporalAccessor) { @@ -249,7 +239,7 @@ public class JSONWriter extends Writer { } else if (value instanceof JSONString) { writeJSONStringValue((JSONString) value); } else { - writeStrValue(value.toString()); + writeQuoteStrValue(value.toString()); } return this; @@ -297,7 +287,7 @@ public class JSONWriter extends Writer { //noinspection resource writeRaw(valueStr); } else { - writeStrValue(jsonString.toString()); + writeQuoteStrValue(jsonString.toString()); } } @@ -309,7 +299,7 @@ public class JSONWriter extends Writer { * * @param csq 字符串 */ - private void writeStrValue(final String csq) { + private void writeQuoteStrValue(final String csq) { try { JSONUtil.quote(csq, writer); } catch (final IOException e) { diff --git a/hutool-json/src/main/java/cn/hutool/json/serialize/TemporalAccessorSerializer.java b/hutool-json/src/main/java/cn/hutool/json/serialize/TemporalAccessorSerializer.java index 6272e7b31..8695abb78 100755 --- a/hutool-json/src/main/java/cn/hutool/json/serialize/TemporalAccessorSerializer.java +++ b/hutool-json/src/main/java/cn/hutool/json/serialize/TemporalAccessorSerializer.java @@ -15,7 +15,7 @@ import java.time.temporal.TemporalAccessor; * @author looly * @since 5.7.22 */ -public class TemporalAccessorSerializer implements JSONObjectSerializer, JSONDeserializer { +public class TemporalAccessorSerializer implements JSONSerializer, JSONDeserializer { private static final String YEAR_KEY = "year"; private static final String MONTH_KEY = "month"; diff --git a/hutool-json/src/test/java/cn/hutool/json/CustomSerializeTest.java b/hutool-json/src/test/java/cn/hutool/json/CustomSerializeTest.java index 873682e84..a8a339053 100644 --- a/hutool-json/src/test/java/cn/hutool/json/CustomSerializeTest.java +++ b/hutool-json/src/test/java/cn/hutool/json/CustomSerializeTest.java @@ -3,16 +3,20 @@ package cn.hutool.json; import cn.hutool.json.serialize.JSONObjectSerializer; import lombok.ToString; import org.junit.Assert; +import org.junit.Before; import org.junit.Test; import java.util.Date; public class CustomSerializeTest { + @Before + public void init(){ + JSONUtil.putSerializer(CustomBean.class, (JSONObjectSerializer) (json, bean) -> json.set("customName", bean.name)); + } + @Test public void serializeTest() { - JSONUtil.putSerializer(CustomBean.class, (JSONObjectSerializer) (json, bean) -> json.set("customName", bean.name)); - final CustomBean customBean = new CustomBean(); customBean.name = "testName"; @@ -20,6 +24,15 @@ public class CustomSerializeTest { Assert.assertEquals("testName", obj.getStr("customName")); } + @Test + public void putTest() { + final CustomBean customBean = new CustomBean(); + customBean.name = "testName"; + + final JSONObject obj = JSONUtil.createObj().set("customBean", customBean); + Assert.assertEquals("testName", obj.getJSONObject("customBean").getStr("customName")); + } + @Test public void deserializeTest() { JSONUtil.putDeserializer(CustomBean.class, json -> { diff --git a/hutool-json/src/test/java/cn/hutool/json/Issue2090Test.java b/hutool-json/src/test/java/cn/hutool/json/Issue2090Test.java index c853a1583..d6ff166d7 100755 --- a/hutool-json/src/test/java/cn/hutool/json/Issue2090Test.java +++ b/hutool-json/src/test/java/cn/hutool/json/Issue2090Test.java @@ -1,5 +1,6 @@ package cn.hutool.json; +import cn.hutool.core.lang.Console; import lombok.Data; import org.junit.Assert; import org.junit.Test; @@ -10,7 +11,7 @@ import java.time.LocalTime; import java.time.Month; /** - * https://github.com/dromara/hutool/issues/2090 + * https://github.com/dromara/hutool/issues/2090 */ public class Issue2090Test { @@ -20,6 +21,7 @@ public class Issue2090Test { test.setLocalDate(LocalDate.now()); final JSONObject json = JSONUtil.parseObj(test); + Console.log(json); final TestBean test1 = json.toBean(TestBean.class); Assert.assertEquals(test, test1); } diff --git a/hutool-json/src/test/java/cn/hutool/json/IssuesI44E4HTest.java b/hutool-json/src/test/java/cn/hutool/json/IssuesI44E4HTest.java index a053cd844..101215859 100755 --- a/hutool-json/src/test/java/cn/hutool/json/IssuesI44E4HTest.java +++ b/hutool-json/src/test/java/cn/hutool/json/IssuesI44E4HTest.java @@ -15,7 +15,7 @@ public class IssuesI44E4HTest { @Test public void deserializerTest(){ - GlobalSerializeMapping.put(TestDto.class, (JSONDeserializer) json -> { + GlobalSerializeMapping.putDeserializer(TestDto.class, (JSONDeserializer) json -> { final TestDto testDto = new TestDto(); testDto.setMd(new AcBizModuleMd("name1", ((JSONObject)json).getStr("md"))); return testDto; diff --git a/hutool-json/src/test/java/cn/hutool/json/JSONObjectTest.java b/hutool-json/src/test/java/cn/hutool/json/JSONObjectTest.java index 9e7713e25..1c4bf3c17 100755 --- a/hutool-json/src/test/java/cn/hutool/json/JSONObjectTest.java +++ b/hutool-json/src/test/java/cn/hutool/json/JSONObjectTest.java @@ -5,9 +5,7 @@ import cn.hutool.core.annotation.PropIgnore; import cn.hutool.core.collection.ListUtil; import cn.hutool.core.date.DatePattern; import cn.hutool.core.date.DateUtil; -import cn.hutool.core.io.FileUtil; import cn.hutool.core.io.resource.ResourceUtil; -import cn.hutool.core.lang.Console; import cn.hutool.core.map.MapUtil; import cn.hutool.core.text.StrUtil; import cn.hutool.core.util.ObjUtil; @@ -24,7 +22,6 @@ import cn.hutool.json.test.bean.report.StepReport; import cn.hutool.json.test.bean.report.SuiteReport; import lombok.Data; import org.junit.Assert; -import org.junit.Ignore; import org.junit.Test; import java.io.ByteArrayInputStream; @@ -47,13 +44,13 @@ import java.util.Set; public class JSONObjectTest { @Test - @Ignore public void toStringTest() { final String str = "{\"code\": 500, \"data\":null}"; + //noinspection MismatchedQueryAndUpdateOfCollection final JSONObject jsonObject = new JSONObject(str); - Console.log(jsonObject); + Assert.assertEquals("{\"code\":500,\"data\":null}", jsonObject.toString()); jsonObject.getConfig().setIgnoreNullValue(true); - Console.log(jsonObject.toStringPretty()); + Assert.assertEquals("{\"code\":500}", jsonObject.toString()); } @Test @@ -176,16 +173,6 @@ public class JSONObjectTest { Assert.assertEquals(new JSONArray(), json.getJSONObject("data").getJSONArray("cards")); } - @Test - @Ignore - public void parseStringWithBomTest() { - final String jsonStr = FileUtil.readUtf8String("f:/test/jsontest.txt"); - final JSONObject json = new JSONObject(jsonStr); - final JSONObject json2 = JSONUtil.parseObj(json); - Console.log(json); - Console.log(json2); - } - @Test public void parseStringWithSlashTest() { //在5.3.2之前,中的/会被转义,修复此bug的单元测试 diff --git a/hutool-json/src/test/java/cn/hutool/json/JSONUtilTest.java b/hutool-json/src/test/java/cn/hutool/json/JSONUtilTest.java index 1c985bf77..16525c648 100644 --- a/hutool-json/src/test/java/cn/hutool/json/JSONUtilTest.java +++ b/hutool-json/src/test/java/cn/hutool/json/JSONUtilTest.java @@ -2,9 +2,9 @@ package cn.hutool.json; import cn.hutool.core.collection.ListUtil; import cn.hutool.core.date.DateUtil; -import cn.hutool.core.lang.Console; import cn.hutool.core.map.MapUtil; import cn.hutool.core.math.NumberUtil; +import cn.hutool.json.serialize.JSONString; import cn.hutool.json.test.bean.Price; import cn.hutool.json.test.bean.UserA; import cn.hutool.json.test.bean.UserC; @@ -25,8 +25,7 @@ public class JSONUtilTest { */ @Test(expected = JSONException.class) public void parseTest() { - final JSONArray jsonArray = JSONUtil.parseArray("[{\"a\":\"a\\x]"); - Console.log(jsonArray); + JSONUtil.parseArray("[{\"a\":\"a\\x]"); } /** @@ -107,6 +106,7 @@ public class JSONUtilTest { @Test public void toJsonStrFromSortedTest() { + //noinspection SerializableInnerClassWithNonSerializableOuterClass final SortedMap sortedMap = new TreeMap() { private static final long serialVersionUID = 1L; @@ -236,14 +236,14 @@ public class JSONUtilTest { @Test public void toJsonStrOfStringTest(){ - String a = "a"; + final String a = "a"; final String s = JSONUtil.toJsonStr(a); Assert.assertEquals(a, s); } @Test public void toJsonStrOfNumberTest(){ - int a = 1; + final int a = 1; final String s = JSONUtil.toJsonStr(a); Assert.assertEquals("1", s); }