From c9eb040b70b956ed95d7554b31baf2a048318fcb Mon Sep 17 00:00:00 2001 From: Looly Date: Thu, 26 Sep 2024 13:44:12 +0800 Subject: [PATCH] JSONFactory --- .../dromara/hutool/http/IssueIAFKWPTest.java | 3 +- .../org/dromara/hutool/json/JSONFactory.java | 180 ++++++++++++++++++ .../org/dromara/hutool/json/JSONUtil.java | 37 +--- .../hutool/json/serializer/JSONMapper.java | 7 +- .../json/serializer/TypeAdapterManager.java | 10 +- .../serializer/impl/ResourceSerializer.java | 59 ++++++ .../hutool/json/xml/JSONXMLParser.java | 3 +- 7 files changed, 260 insertions(+), 39 deletions(-) create mode 100644 hutool-json/src/main/java/org/dromara/hutool/json/JSONFactory.java create mode 100644 hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/ResourceSerializer.java diff --git a/hutool-http/src/test/java/org/dromara/hutool/http/IssueIAFKWPTest.java b/hutool-http/src/test/java/org/dromara/hutool/http/IssueIAFKWPTest.java index fd0a2c380..e49ab65bf 100644 --- a/hutool-http/src/test/java/org/dromara/hutool/http/IssueIAFKWPTest.java +++ b/hutool-http/src/test/java/org/dromara/hutool/http/IssueIAFKWPTest.java @@ -20,6 +20,7 @@ import org.dromara.hutool.core.collection.ListUtil; import org.dromara.hutool.core.net.url.UrlQuery; import org.dromara.hutool.core.util.CharsetUtil; import org.dromara.hutool.json.JSONObject; +import org.dromara.hutool.json.JSONUtil; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -30,7 +31,7 @@ public class IssueIAFKWPTest { @Test void urlWithFormTest() { - final JSONObject obj = new JSONObject(); + final JSONObject obj = JSONUtil.ofObj(); obj.set("fields", ListUtil.of("1", "2", "good")); final Map params = new HashMap<>(); diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/JSONFactory.java b/hutool-json/src/main/java/org/dromara/hutool/json/JSONFactory.java new file mode 100644 index 000000000..eaf37af5d --- /dev/null +++ b/hutool-json/src/main/java/org/dromara/hutool/json/JSONFactory.java @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2024 Hutool Team and hutool.cn + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.dromara.hutool.json; + +import org.dromara.hutool.core.lang.mutable.MutableEntry; +import org.dromara.hutool.core.util.ObjUtil; +import org.dromara.hutool.json.serializer.JSONMapper; + +import java.io.ByteArrayInputStream; +import java.lang.reflect.Type; +import java.util.function.Predicate; + +/** + * JSON工厂类,用于JSON创建、解析、转换为Bean等功能 + * + * @author looly + * @since 6.0.0 + */ +public class JSONFactory { + + /** + * 创建JSON工厂 + * + * @param config JSON配置 + * @param predicate 键值对过滤器,{@code null}表示不过滤 + * @return JSON工厂 + */ + public static JSONFactory of(final JSONConfig config, final Predicate> predicate) { + return new JSONFactory(config, predicate); + } + + private final JSONConfig config; + private final Predicate> predicate; + private final JSONMapper mapper; + + /** + * 构造 + * + * @param config 配置项 + * @param predicate 键值对过滤器,用于过滤掉不需要的键值对,例如:过滤掉值为null的键值对 + */ + public JSONFactory(final JSONConfig config, final Predicate> predicate) { + this.config = ObjUtil.defaultIfNull(config, JSONConfig::of); + this.predicate = predicate; + this.mapper = JSONMapper.of(config, predicate); + } + + /** + * 获取{@link JSONMapper} + * + * @return {@link JSONMapper} + */ + public JSONMapper getMapper() { + return mapper; + } + + // region ----- of + + /** + * 创建JSONObject + * + * @return JSONObject + */ + public JSONObject ofObj() { + return new JSONObject(this.config); + } + + /** + * 创建JSONArray + * + * @return JSONArray + */ + public JSONArray ofArray() { + return new JSONArray(this.config); + } + + /** + * 创建JSONPrimitive + * + * @param value 值 + * @return JSONPrimitive + */ + public JSONPrimitive ofPrimitive(final Object value) { + return new JSONPrimitive(value, this.config); + } + // endregion + + // region ----- parse + + /** + * JSON字符串转JSONObject对象 + * + * @param obj Bean对象或者Map + * @return JSONObject + */ + public JSONObject parseObj(Object obj) { + if (obj instanceof byte[]) { + obj = new ByteArrayInputStream((byte[]) obj); + } + + final JSONMapper jsonMapper = JSONMapper.of(config, predicate); + if (obj instanceof CharSequence) { + return (JSONObject) jsonMapper.map((CharSequence) obj); + } + return jsonMapper.mapObj(obj); + } + + /** + * 对象转{@link JSONArray},支持: + *
    + *
  • {@link JSONObject} 遍历Entry,结果为:[{k1:v1}, {k2: v2}, ...]
  • + *
  • {@link CharSequence},解析[...]字符串
  • + *
  • 其它支持和自定义的对象(如集合、数组等)
  • + *
+ * + * @param obj 数组或集合对象 + * @return JSONArray + */ + public JSONArray parseArray(final Object obj) { + if (obj instanceof JSONObject) { + final JSONMapper jsonMapper = JSONMapper.of(config, predicate); + return jsonMapper.mapFromJSONObject((JSONObject) obj); + } + + final JSONMapper jsonMapper = JSONMapper.of(config, predicate); + if (obj instanceof CharSequence) { + return (JSONArray) jsonMapper.map((CharSequence) obj); + } + return jsonMapper.mapArray(obj); + } + + /** + * 转换对象为JSON,如果用户不配置JSONConfig,则JSON的有序与否与传入对象有关。
+ * 支持的对象: + *
    + *
  • String: 转换为相应的对象
  • + *
  • Array、Iterable、Iterator:转换为JSONArray
  • + *
  • Bean对象:转为JSONObject
  • + *
+ * + * @param obj 对象 + * @return JSON(JSONObject or JSONArray) + */ + public JSON parse(final Object obj) { + if (obj instanceof CharSequence) { + return mapper.map((CharSequence) obj); + } + return mapper.map(obj); + } + // endregion + + // region ----- toBean + + /** + * 将JSON转换为指定类型的Bean对象 + * + * @param json JSON + * @param type Bean类型,泛型对象使用{@link org.dromara.hutool.core.reflect.TypeReference} + * @param 泛型类型 + * @return Bean对象 + */ + public T toBean(final JSON json, final Type type) { + return mapper.toBean(json, type); + } + // endregion +} 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 609b0fca8..f272e9c96 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 @@ -23,12 +23,10 @@ import org.dromara.hutool.core.lang.mutable.MutableEntry; import org.dromara.hutool.core.reflect.TypeReference; import org.dromara.hutool.core.text.StrUtil; import org.dromara.hutool.core.util.ObjUtil; -import org.dromara.hutool.json.serializer.JSONMapper; import org.dromara.hutool.json.support.JSONStrFormatter; import org.dromara.hutool.json.writer.JSONWriter; import org.dromara.hutool.json.xml.JSONXMLUtil; -import java.io.ByteArrayInputStream; import java.io.File; import java.io.Writer; import java.lang.reflect.Type; @@ -149,24 +147,15 @@ public class JSONUtil { } /** - * JSON字符串转JSONObject对象
- * 此方法会忽略空值,但是对JSON字符串不影响 + * 对象转JSONObject对象 * * @param obj Bean对象或者Map * @param config JSON配置 * @param predicate 键值对过滤编辑器,可以通过实现此接口,完成解析前对键值对的过滤和修改操作,{@link Predicate#test(Object)}为{@code true}保留 * @return JSONObject */ - public static JSONObject parseObj(Object obj, final JSONConfig config, final Predicate> predicate) { - if (obj instanceof byte[]) { - obj = new ByteArrayInputStream((byte[]) obj); - } - - final JSONMapper jsonMapper = JSONMapper.of(config, predicate); - if (obj instanceof CharSequence) { - return (JSONObject) jsonMapper.map((CharSequence) obj); - } - return jsonMapper.mapObj(obj); + public static JSONObject parseObj(final Object obj, final JSONConfig config, final Predicate> predicate) { + return JSONFactory.of(config, predicate).parseObj(obj); } /** @@ -174,7 +163,6 @@ public class JSONUtil { * * @param obj 数组或集合对象或字符串等 * @return JSONArray - * @since 3.0.8 */ public static JSONArray parseArray(final Object obj) { return parseArray(obj, null); @@ -193,7 +181,7 @@ public class JSONUtil { } /** - * JSON字符串转JSONArray + * 对象转JSONArray * * @param obj 数组或集合对象 * @param config JSON配置 @@ -202,16 +190,7 @@ public class JSONUtil { * @since 5.3.1 */ public static JSONArray parseArray(final Object obj, final JSONConfig config, final Predicate> predicate) { - if (obj instanceof JSONObject) { - final JSONMapper jsonMapper = JSONMapper.of(config, predicate); - return jsonMapper.mapFromJSONObject((JSONObject) obj); - } - - final JSONMapper jsonMapper = JSONMapper.of(config, predicate); - if (obj instanceof CharSequence) { - return (JSONArray) jsonMapper.map((CharSequence) obj); - } - return jsonMapper.mapArray(obj); + return JSONFactory.of(config, predicate).parseArray(obj); } /** @@ -262,11 +241,7 @@ public class JSONUtil { * @return JSON(JSONObject or JSONArray) */ public static JSON parse(final Object obj, final JSONConfig config, final Predicate> predicate) { - final JSONMapper jsonMapper = JSONMapper.of(config, predicate); - if (obj instanceof CharSequence) { - return jsonMapper.map((CharSequence) obj); - } - return jsonMapper.map(obj); + return JSONFactory.of(config, predicate).parse(obj); } /** 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 892b550d5..008e79661 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 @@ -18,6 +18,7 @@ package org.dromara.hutool.json.serializer; import org.dromara.hutool.core.lang.Opt; import org.dromara.hutool.core.lang.mutable.MutableEntry; +import org.dromara.hutool.core.reflect.TypeReference; import org.dromara.hutool.core.text.StrUtil; import org.dromara.hutool.core.util.ObjUtil; import org.dromara.hutool.json.*; @@ -104,7 +105,11 @@ public class JSONMapper implements Serializable { * @return 实体类对象 */ @SuppressWarnings("unchecked") - public T toBean(final JSON json, final Type type) { + public T toBean(final JSON json, Type type) { + if (type instanceof TypeReference) { + type = ((TypeReference) type).getType(); + } + if (null == type || Object.class == type) { if (json instanceof JSONPrimitive) { return (T) ((JSONPrimitive) json).getValue(); diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/serializer/TypeAdapterManager.java b/hutool-json/src/main/java/org/dromara/hutool/json/serializer/TypeAdapterManager.java index 963ba75af..13802bc46 100644 --- a/hutool-json/src/main/java/org/dromara/hutool/json/serializer/TypeAdapterManager.java +++ b/hutool-json/src/main/java/org/dromara/hutool/json/serializer/TypeAdapterManager.java @@ -35,18 +35,17 @@ import java.util.Map; import java.util.Set; /** - * JSON序列化和反序列化管理器,用于管理JSON序列化器,注册和注销自定义序列化器和反序列化器。
+ * JSON适配器(序列化和反序列化)管理器,用于管理JSON序列化器,注册和注销自定义序列化器和反序列化器。
* 此管理器管理着两种类型的序列化器和反序列化器: *
    *
  • 类型精准匹配方式。通过Java对象类型匹配,只会匹配查找的类型,而不匹配子类。可以调用{@link #register(Type, TypeAdapter)}注册。
  • *
  • 匹配器(Matcher)方式。通过判断序列化和反序列化器中match方法,找到自定义的序列化和反序列化器,可以调用{@link #register(TypeAdapter)}注册。
  • *
*

- * 管理器的使用分为三种方式: + * 管理器的使用分为两种方式: *

    *
  • 全局模式: 使用{@link TypeAdapterManager#getInstance()}调用单例,全局可用。
  • - *
  • 实例模式: 使用{@link TypeAdapterManager#of()}创建实例,局部可用。
  • - *
  • 自定义模式:使用{@code new SerializerManager()}创建实例,不加载默认的转换器。
  • + *
  • 实例模式: 使用{@link TypeAdapterManager#of()}创建实例,局部可用,不加载默认的转换器。
  • *
* * @author looly @@ -285,8 +284,9 @@ public class TypeAdapterManager { private static TypeAdapterManager registerDefault(final TypeAdapterManager manager) { // 自定义序列化器 - manager.register(ResourceBundleSerializer.INSTANCE); + manager.register(ResourceSerializer.INSTANCE); manager.register(TokenerSerializer.INSTANCE); + manager.register(ResourceBundleSerializer.INSTANCE); // 自定义反序列化器 manager.register(KBeanDeserializer.INSTANCE); diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/ResourceSerializer.java b/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/ResourceSerializer.java new file mode 100644 index 000000000..ca4fd2c95 --- /dev/null +++ b/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/ResourceSerializer.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2024 Hutool Team and hutool.cn + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.dromara.hutool.json.serializer.impl; + +import org.dromara.hutool.core.io.resource.Resource; +import org.dromara.hutool.json.JSON; +import org.dromara.hutool.json.JSONConfig; +import org.dromara.hutool.json.reader.JSONParser; +import org.dromara.hutool.json.reader.JSONTokener; +import org.dromara.hutool.json.serializer.JSONContext; +import org.dromara.hutool.json.serializer.MatcherJSONSerializer; + +/** + * {@link Resource}序列化器 + * + * @author looly + * @since 6.0.0 + */ +public class ResourceSerializer implements MatcherJSONSerializer { + + /** + * 单例 + */ + public static final ResourceSerializer INSTANCE = new ResourceSerializer(); + + @Override + public boolean match(final Object bean, final JSONContext context) { + return bean instanceof Resource; + } + + @Override + public JSON serialize(final Resource bean, final JSONContext context) { + return mapFromTokener(new JSONTokener(bean.getStream()), context.config()); + } + + /** + * 从{@link JSONTokener} 中读取JSON字符串,并转换为JSON + * + * @param tokener {@link JSONTokener} + * @return JSON + */ + private JSON mapFromTokener(final JSONTokener tokener, final JSONConfig config) { + return JSONParser.of(tokener, config).setPredicate(null).parse(); + } +} 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 65fd63a94..54a2ad6bd 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 @@ -23,6 +23,7 @@ import org.dromara.hutool.core.xml.XmlConstants; import org.dromara.hutool.json.InternalJSONUtil; import org.dromara.hutool.json.JSONException; import org.dromara.hutool.json.JSONObject; +import org.dromara.hutool.json.JSONUtil; import java.util.function.Predicate; @@ -156,7 +157,7 @@ public class JSONXMLParser { } else { tagName = (String) token; token = null; - jsonobject = new JSONObject(); + jsonobject = JSONUtil.ofObj(context.config()); final boolean keepStrings = parseConfig.isKeepStrings(); for (; ; ) { if (token == null) {