From 571d76f578f6a017d814ae9015141b32c610ecd4 Mon Sep 17 00:00:00 2001 From: Looly Date: Mon, 18 Apr 2022 21:31:02 +0800 Subject: [PATCH] add ObjectMapper --- CHANGELOG.md | 3 +- .../main/java/cn/hutool/json/JSONArray.java | 76 +----- .../main/java/cn/hutool/json/JSONObject.java | 135 +--------- .../java/cn/hutool/json/ObjectMapper.java | 236 ++++++++++++++++++ 4 files changed, 247 insertions(+), 203 deletions(-) create mode 100755 hutool-json/src/main/java/cn/hutool/json/ObjectMapper.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b7f33a87..8bc455df6 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ ------------------------------------------------------------------------------------------------------------- -# 5.8.0.M4 (2022-04-17) +# 5.8.0.M4 (2022-04-18) ### ❌不兼容特性 * 【json 】 【可能兼容问题】JSONArray删除部分构造 @@ -14,6 +14,7 @@ * 【json 】 JSON新增在解析时的过滤方法(issue#I52O85@Gitee) * 【core 】 添加ArrayUtil.distinct、CollUtil.distinct重载(issue#2256@Github) * 【core 】 添加TransMap、FuncMap、ReferenceConcurrentMap、WeakConcurrentMap +* 【json 】 添加ObjectMapper ### 🐞Bug修复 * 【core 】 修复StrUtil.firstNonX非static问题(issue#2257@Github) 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 553bd02f6..809d4c652 100644 --- a/hutool-json/src/main/java/cn/hutool/json/JSONArray.java +++ b/hutool-json/src/main/java/cn/hutool/json/JSONArray.java @@ -1,19 +1,13 @@ package cn.hutool.json; import cn.hutool.core.bean.BeanPath; -import cn.hutool.core.collection.ArrayIter; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.lang.Filter; import cn.hutool.core.lang.mutable.Mutable; import cn.hutool.core.lang.mutable.MutableObj; import cn.hutool.core.lang.mutable.MutablePair; import cn.hutool.core.text.StrJoiner; -import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.ObjectUtil; -import cn.hutool.core.util.StrUtil; -import cn.hutool.core.util.TypeUtil; -import cn.hutool.json.serialize.GlobalSerializeMapping; -import cn.hutool.json.serialize.JSONSerializer; import cn.hutool.json.serialize.JSONWriter; import java.io.StringWriter; @@ -171,7 +165,7 @@ public class JSONArray implements JSON, JSONGetter, List, Rando */ public JSONArray(Object object, JSONConfig jsonConfig, Filter> filter) throws JSONException { this(DEFAULT_CAPACITY, jsonConfig); - init(object, filter); + ObjectMapper.of(object).map(this, filter); } // endregion @@ -588,7 +582,6 @@ public class JSONArray implements JSON, JSONGetter, List, Rando clone.rawList = ObjectUtil.clone(this.rawList); return clone; } - // ------------------------------------------------------------------------------------------------- Private method start /** * 原始添加,添加的对象不做任何处理 @@ -612,71 +605,4 @@ public class JSONArray implements JSON, JSONGetter, List, Rando } return this.rawList.add(obj); } - - /** - * 初始化 - * - * @param source 数组或集合或JSON数组字符串 - * @param filter 键值对过滤编辑器,可以通过实现此接口,完成解析前对值的过滤和修改操作,{@code null}表示不过滤 - * @throws JSONException 非数组或集合 - */ - @SuppressWarnings({"rawtypes", "unchecked"}) - private void init(Object source, Filter> filter) throws JSONException { - if (null == source) { - return; - } - - final JSONSerializer serializer = GlobalSerializeMapping.getSerializer(source.getClass()); - if (null != serializer && JSONArray.class.equals(TypeUtil.getTypeArgument(serializer.getClass()))) { - // 自定义序列化 - serializer.serialize(this, source); - } else if (source instanceof CharSequence) { - // JSON字符串 - initFromStr((CharSequence) source, filter); - } else if (source instanceof JSONTokener) { - initFromTokener((JSONTokener) source, filter); - } else { - 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) { - this.addRaw(JSONUtil.wrap(next, this.config), filter); - } - } - } - } - - /** - * 初始化 - * - * @param source JSON字符串 - */ - private void initFromStr(CharSequence source, Filter> filter) { - if (null != source) { - initFromTokener(new JSONTokener(StrUtil.trim(source), this.config), filter); - } - } - - /** - * 初始化 - * - * @param x {@link JSONTokener} - * @param filter 键值对过滤编辑器,可以通过实现此接口,完成解析前对值的过滤和修改操作,{@code null}表示不过滤 - */ - private void initFromTokener(JSONTokener x, Filter> filter) { - JSONParser.of(x).parseTo(this, filter); - } - // ------------------------------------------------------------------------------------------------- Private method end } 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 53b7deb26..921d19df8 100644 --- a/hutool-json/src/main/java/cn/hutool/json/JSONObject.java +++ b/hutool-json/src/main/java/cn/hutool/json/JSONObject.java @@ -1,9 +1,7 @@ package cn.hutool.json; import cn.hutool.core.bean.BeanPath; -import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollectionUtil; -import cn.hutool.core.convert.Convert; import cn.hutool.core.lang.Filter; import cn.hutool.core.lang.mutable.MutablePair; import cn.hutool.core.map.CaseInsensitiveMap; @@ -12,10 +10,6 @@ import cn.hutool.core.map.MapWrapper; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ReflectUtil; -import cn.hutool.core.util.StrUtil; -import cn.hutool.json.serialize.GlobalSerializeMapping; -import cn.hutool.json.serialize.JSONObjectSerializer; -import cn.hutool.json.serialize.JSONSerializer; import cn.hutool.json.serialize.JSONWriter; import java.io.StringWriter; @@ -24,9 +18,7 @@ import java.lang.reflect.Type; import java.math.BigDecimal; import java.math.BigInteger; import java.util.Collection; -import java.util.Enumeration; import java.util.Map; -import java.util.ResourceBundle; /** * JSON对象
@@ -216,7 +208,7 @@ public class JSONObject extends MapWrapper implements JSON, JSON */ public JSONObject(Object source, JSONConfig config, Filter> filter) { this(DEFAULT_CAPACITY, config); - init(source, filter); + ObjectMapper.of(source).map(this, filter); } /** @@ -230,26 +222,26 @@ public class JSONObject extends MapWrapper implements JSON, JSON * KEY或VALUE任意一个为null则不加入,字段不存在也不加入
* 若names列表为空,则字段全部加入 * - * @param obj 包含需要字段的Bean对象或者Map对象 - * @param names 需要构建JSONObject的字段名列表 + * @param source 包含需要字段的Bean对象或者Map对象 + * @param names 需要构建JSONObject的字段名列表 */ - public JSONObject(Object obj, String... names) { + public JSONObject(Object source, String... names) { this(); if (ArrayUtil.isEmpty(names)) { - init(obj, null); + ObjectMapper.of(source).map(this, null); return; } - if (obj instanceof Map) { + if (source instanceof Map) { Object value; for (String name : names) { - value = ((Map) obj).get(name); + value = ((Map) source).get(name); this.putOnce(name, value); } } else { for (String name : names) { try { - this.putOpt(name, ReflectUtil.getFieldValue(obj, name)); + this.putOpt(name, ReflectUtil.getFieldValue(source, name)); } catch (Exception ignore) { // ignore } @@ -598,115 +590,4 @@ public class JSONObject extends MapWrapper implements JSON, JSON clone.config = this.config; return clone; } - - // ------------------------------------------------------------------------------------------------- Private method start - - /** - * Bean对象转Map - * - * @param bean Bean对象 - */ - private void populateMap(Object bean) { - BeanUtil.beanToMap(bean, this, InternalJSONUtil.toCopyOptions(config)); - } - - /** - * 初始化 - *
    - *
  1. value为Map,将键值对加入JSON对象
  2. - *
  3. value为JSON字符串(CharSequence),使用JSONTokener解析
  4. - *
  5. value为JSONTokener,直接解析
  6. - *
  7. value为普通JavaBean,如果为普通的JavaBean,调用其getters方法(getXXX或者isXXX)获得值,加入到JSON对象。例如:如果JavaBean对象中有个方法getName(),值为"张三",获得的键值对为:name: "张三"
  8. - *
- * - * @param source JavaBean或者Map对象或者String - * @param filter 键值对过滤编辑器,可以通过实现此接口,完成解析前对键值对的过滤和修改操作 - */ - @SuppressWarnings({"rawtypes", "unchecked"}) - private void init(Object source, Filter> filter) { - if (null == source) { - return; - } - - // 自定义序列化 - final JSONSerializer serializer = GlobalSerializeMapping.getSerializer(source.getClass()); - if (serializer instanceof JSONObjectSerializer) { - serializer.serialize(this, source); - return; - } - - if (ArrayUtil.isArray(source) || source instanceof JSONArray) { - // 不支持集合类型转换为JSONObject - throw new JSONException("Unsupported type [{}] to JSONObject!", source.getClass()); - } - - if (source instanceof Map) { - // Map - for (final Entry e : ((Map) source).entrySet()) { - this.set(Convert.toStr(e.getKey()), e.getValue(), filter, false); - } - } else if (source instanceof Map.Entry) { - final Map.Entry entry = (Map.Entry) source; - this.set(Convert.toStr(entry.getKey()), entry.getValue(), filter, false); - } else if (source instanceof CharSequence) { - // 可能为JSON字符串 - initFromStr((CharSequence) source, filter); - } else if (source instanceof JSONTokener) { - // JSONTokener - initFromTokener((JSONTokener) source, filter); - } else if (source instanceof ResourceBundle) { - // JSONTokener - initFromResourceBundle((ResourceBundle) source, filter); - } else if (BeanUtil.isReadableBean(source.getClass())) { - // 普通Bean,过滤器无效 - this.populateMap(source); - } else { - // 不支持对象类型转换为JSONObject - throw new JSONException("Unsupported type [{}] to JSONObject!", source.getClass()); - } - } - - /** - * 初始化 - * - * @param bundle ResourceBundle - * @param filter 键值对过滤编辑器,可以通过实现此接口,完成解析前对键值对的过滤和修改操作,{@code null}表示不过滤 - * @since 5.3.1 - */ - private void initFromResourceBundle(ResourceBundle bundle, Filter> filter) { - Enumeration keys = bundle.getKeys(); - while (keys.hasMoreElements()) { - String key = keys.nextElement(); - if (key != null) { - InternalJSONUtil.propertyPut(this, key, bundle.getString(key), filter); - } - } - } - - /** - * 初始化,可以判断字符串为JSON或者XML - * - * @param source JSON字符串 - * @param filter 键值对过滤编辑器,可以通过实现此接口,完成解析前对键值对的过滤和修改操作,{@code null}表示不过滤 - */ - private void initFromStr(CharSequence source, Filter> filter) { - final String jsonStr = StrUtil.trim(source); - if (StrUtil.startWith(jsonStr, '<')) { - // 可能为XML - XML.toJSONObject(this, jsonStr, false); - return; - } - initFromTokener(new JSONTokener(StrUtil.trim(source), this.config), filter); - } - - /** - * 初始化 - * - * @param x JSONTokener - * @param filter 键值对过滤编辑器,可以通过实现此接口,完成解析前对键值对的过滤和修改操作 - */ - private void initFromTokener(JSONTokener x, Filter> filter) { - JSONParser.of(x).parseTo(this, filter); - } - // ------------------------------------------------------------------------------------------------- Private method end } diff --git a/hutool-json/src/main/java/cn/hutool/json/ObjectMapper.java b/hutool-json/src/main/java/cn/hutool/json/ObjectMapper.java new file mode 100755 index 000000000..d55146e91 --- /dev/null +++ b/hutool-json/src/main/java/cn/hutool/json/ObjectMapper.java @@ -0,0 +1,236 @@ +package cn.hutool.json; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.ArrayIter; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.lang.Filter; +import cn.hutool.core.lang.mutable.Mutable; +import cn.hutool.core.lang.mutable.MutablePair; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.core.util.TypeUtil; +import cn.hutool.json.serialize.GlobalSerializeMapping; +import cn.hutool.json.serialize.JSONObjectSerializer; +import cn.hutool.json.serialize.JSONSerializer; + +import java.util.Enumeration; +import java.util.Iterator; +import java.util.Map; +import java.util.ResourceBundle; + +/** + * 对象和JSON映射器,用于转换对象为JSON,支持: + *
    + *
  • Map 转 JSONObject,将键值对加入JSON对象
  • + *
  • Map.Entry 转 JSONObject
  • + *
  • CharSequence 转 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(Object source) { + return new ObjectMapper(source); + } + + private final Object source; + + /** + * 构造 + * + * @param source 来源对象 + */ + public ObjectMapper(Object source) { + this.source = source; + } + + /** + * 将给定对象转换为{@link JSONObject} + * + * @param jsonObject 目标{@link JSONObject} + * @param filter 键值对过滤编辑器,可以通过实现此接口,完成解析前对键值对的过滤和修改操作 + */ + @SuppressWarnings({"rawtypes", "unchecked"}) + public void map(JSONObject jsonObject, Filter> filter) { + 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 (ArrayUtil.isArray(source) || 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(), filter, false); + } + } else if (source instanceof Map.Entry) { + final Map.Entry entry = (Map.Entry) source; + jsonObject.set(Convert.toStr(entry.getKey()), entry.getValue(), filter, false); + } else if (source instanceof CharSequence) { + // 可能为JSON字符串 + mapFromStr((CharSequence) source, jsonObject, filter); + } else if (source instanceof JSONTokener) { + // JSONTokener + mapFromTokener((JSONTokener) source, jsonObject, filter); + } else if (source instanceof ResourceBundle) { + // JSONTokener + mapFromResourceBundle((ResourceBundle) source, jsonObject, filter); + } else if (BeanUtil.isReadableBean(source.getClass())) { + // 普通Bean + // TODO 过滤器对Bean无效,需补充。 + mapFromBean(source, jsonObject); + } else { + // 不支持对象类型转换为JSONObject + throw new JSONException("Unsupported type [{}] to JSONObject!", source.getClass()); + } + } + + /** + * 初始化 + * + * @param jsonArray 目标{@link JSONArray} + * @param filter 键值对过滤编辑器,可以通过实现此接口,完成解析前对值的过滤和修改操作,{@code null}表示不过滤 + * @throws JSONException 非数组或集合 + */ + @SuppressWarnings({"rawtypes", "unchecked"}) + public void map(JSONArray jsonArray, Filter> filter) 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, filter); + } else if (source instanceof JSONTokener) { + mapFromTokener((JSONTokener) source, jsonArray, filter); + } else { + 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), filter); + } + } + } + } + + /** + * 从{@link ResourceBundle}转换 + * + * @param bundle ResourceBundle + * @param jsonObject {@link JSONObject} + * @param filter 键值对过滤编辑器,可以通过实现此接口,完成解析前对键值对的过滤和修改操作,{@code null}表示不过滤 + * @since 5.3.1 + */ + private static void mapFromResourceBundle(ResourceBundle bundle, JSONObject jsonObject, Filter> filter) { + Enumeration keys = bundle.getKeys(); + while (keys.hasMoreElements()) { + String key = keys.nextElement(); + if (key != null) { + InternalJSONUtil.propertyPut(jsonObject, key, bundle.getString(key), filter); + } + } + } + + /** + * 从字符串转换 + * + * @param source JSON字符串 + * @param jsonObject {@link JSONObject} + * @param filter 键值对过滤编辑器,可以通过实现此接口,完成解析前对键值对的过滤和修改操作,{@code null}表示不过滤 + */ + private static void mapFromStr(CharSequence source, JSONObject jsonObject, Filter> filter) { + 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, filter); + } + + /** + * 初始化 + * + * @param source JSON字符串 + * @param jsonArray {@link JSONArray} + * @param filter 值过滤编辑器,可以通过实现此接口,完成解析前对值的过滤和修改操作,{@code null}表示不过滤 + */ + private void mapFromStr(CharSequence source, JSONArray jsonArray, Filter> filter) { + if (null != source) { + mapFromTokener(new JSONTokener(StrUtil.trim(source), jsonArray.getConfig()), jsonArray, filter); + } + } + + /** + * 从{@link JSONTokener}转换 + * + * @param x JSONTokener + * @param jsonObject {@link JSONObject} + * @param filter 键值对过滤编辑器,可以通过实现此接口,完成解析前对键值对的过滤和修改操作 + */ + private static void mapFromTokener(JSONTokener x, JSONObject jsonObject, Filter> filter) { + JSONParser.of(x).parseTo(jsonObject, filter); + } + + /** + * 初始化 + * + * @param x {@link JSONTokener} + * @param jsonArray {@link JSONArray} + * @param filter 值过滤编辑器,可以通过实现此接口,完成解析前对值的过滤和修改操作,{@code null}表示不过滤 + */ + private static void mapFromTokener(JSONTokener x, JSONArray jsonArray, Filter> filter) { + JSONParser.of(x).parseTo(jsonArray, filter); + } + + /** + * 从Bean转换 + * + * @param bean Bean对象 + * @param jsonObject {@link JSONObject} + */ + private static void mapFromBean(Object bean, JSONObject jsonObject) { + BeanUtil.beanToMap(bean, jsonObject, InternalJSONUtil.toCopyOptions(jsonObject.getConfig())); + } +}