From 6acf0854d851291f8f94aad38a6501e33fc63242 Mon Sep 17 00:00:00 2001 From: Looly Date: Sat, 16 Apr 2022 11:44:01 +0800 Subject: [PATCH] add filter --- CHANGELOG.md | 2 + .../java/cn/hutool/json/InternalJSONUtil.java | 49 +++++- .../main/java/cn/hutool/json/JSONArray.java | 136 +++++++++------- .../main/java/cn/hutool/json/JSONObject.java | 151 +++++++++++------- .../main/java/cn/hutool/json/JSONParser.java | 21 ++- .../java/cn/hutool/json/JSONArrayTest.java | 28 +++- .../java/cn/hutool/json/JSONObjectTest.java | 22 +++ 7 files changed, 274 insertions(+), 135 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7dd9e5c31..7768b1bd0 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,10 +6,12 @@ # 5.8.0.M4 (2022-04-16) ### ❌不兼容特性 +* 【json 】 【可能兼容问题】JSONArray删除部分构造 ### 🐣新特性 * 【core 】 BeanUtil增加toBean重载(pr#598@Gitee) * 【json 】 新增JSONParser +* 【json 】 JSON新增在解析时的过滤方法(issue#I52O85@Gitee) ### 🐞Bug修复 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 c71277e3a..0229c4e38 100644 --- a/hutool-json/src/main/java/cn/hutool/json/InternalJSONUtil.java +++ b/hutool-json/src/main/java/cn/hutool/json/InternalJSONUtil.java @@ -2,6 +2,10 @@ package cn.hutool.json; import cn.hutool.core.bean.copier.CopyOptions; import cn.hutool.core.convert.Convert; +import cn.hutool.core.lang.Filter; +import cn.hutool.core.lang.mutable.MutablePair; +import cn.hutool.core.map.CaseInsensitiveLinkedMap; +import cn.hutool.core.map.CaseInsensitiveTreeMap; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.CharUtil; import cn.hutool.core.util.NumberUtil; @@ -10,7 +14,10 @@ import cn.hutool.core.util.StrUtil; import java.math.BigDecimal; import java.util.Collection; +import java.util.Comparator; +import java.util.LinkedHashMap; import java.util.Map; +import java.util.TreeMap; /** * 内部JSON工具类,仅用于JSON内部使用 @@ -26,12 +33,14 @@ public final class InternalJSONUtil { * 如果对象是Number 且是 NaN or infinite,将抛出异常 * * @param obj 被检查的对象 + * @return 检测后的值 * @throws JSONException If o is a non-finite number. */ - static void testValidity(Object obj) throws JSONException { + static Object testValidity(Object obj) throws JSONException { if (false == ObjectUtil.isValidIfNumber(obj)) { throw new JSONException("JSON does not allow non-finite numbers."); } + return obj; } /** @@ -123,14 +132,15 @@ public final class InternalJSONUtil { /** * 将Property的键转化为JSON形式
- * 用于识别类似于:com.luxiaolei.package.hutool这类用点隔开的键 + * 用于识别类似于:com.luxiaolei.package.hutool这类用点隔开的键
+ * 注意:不允许重复键 * * @param jsonObject JSONObject * @param key 键 * @param value 值 * @return JSONObject */ - static JSONObject propertyPut(JSONObject jsonObject, Object key, Object value) { + static JSONObject propertyPut(JSONObject jsonObject, Object key, Object value, Filter> filter) { final String[] path = StrUtil.splitToArray(Convert.toStr(key), CharUtil.DOT); int last = path.length - 1; JSONObject target = jsonObject; @@ -139,11 +149,11 @@ public final class InternalJSONUtil { JSONObject nextTarget = target.getJSONObject(segment); if (nextTarget == null) { nextTarget = new JSONObject(target.getConfig()); - target.set(segment, nextTarget); + target.setOnce(segment, nextTarget, filter); } target = nextTarget; } - target.set(path[last], value); + target.setOnce(path[last], value, filter); return jsonObject; } @@ -180,4 +190,33 @@ public final class InternalJSONUtil { .setIgnoreNullValue(config.isIgnoreNullValue()) .setTransientSupport(config.isTransientSupport()); } + + /** + * 根据配置创建对应的原始Map + * + * @param capacity 初始大小 + * @param config JSON配置项,{@code null}则使用默认配置 + * @return Map + */ + static Map createRawMap(int capacity, JSONConfig config) { + Map rawHashMap; + if (null == config) { + config = JSONConfig.create(); + } + final Comparator keyComparator = config.getKeyComparator(); + if (config.isIgnoreCase()) { + if (null != keyComparator) { + rawHashMap = new CaseInsensitiveTreeMap<>(keyComparator); + } else { + rawHashMap = new CaseInsensitiveLinkedMap<>(capacity); + } + } else { + if (null != keyComparator) { + rawHashMap = new TreeMap<>(keyComparator); + } else { + rawHashMap = new LinkedHashMap<>(capacity); + } + } + return rawHashMap; + } } 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 55b367082..553bd02f6 100644 --- a/hutool-json/src/main/java/cn/hutool/json/JSONArray.java +++ b/hutool-json/src/main/java/cn/hutool/json/JSONArray.java @@ -4,6 +4,8 @@ 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; @@ -52,7 +54,7 @@ public class JSONArray implements JSON, JSONGetter, List, Rando */ private final JSONConfig config; - // -------------------------------------------------------------------------------------------------------------------- Constructor start + // region Constructors /** * 构造
@@ -97,52 +99,6 @@ public class JSONArray implements JSON, JSONGetter, List, Rando this.config = ObjectUtil.defaultIfNull(config, JSONConfig::create); } - /** - * 构造
- * 将参数数组中的元素转换为JSON对应的对象加入到JSONArray中 - * - * @param list 初始化的JSON数组 - */ - public JSONArray(Iterable list) { - this(); - for (Object o : list) { - this.add(o); - } - } - - /** - * 构造
- * 将参数数组中的元素转换为JSON对应的对象加入到JSONArray中 - * - * @param list 初始化的JSON数组 - */ - public JSONArray(Collection list) { - this(list.size()); - this.addAll(list); - } - - /** - * 使用 {@link JSONTokener} 做为参数构造 - * - * @param x A {@link JSONTokener} - * @throws JSONException If there is a syntax error. - */ - public JSONArray(JSONTokener x) throws JSONException { - this(); - init(x); - } - - /** - * 从String构造(JSONArray字符串) - * - * @param source JSON数组字符串 - * @throws JSONException If there is a syntax error. - */ - public JSONArray(CharSequence source) throws JSONException { - this(); - init(source); - } - /** * 从对象构造,忽略{@code null}的值
* 支持以下类型的参数: @@ -194,10 +150,30 @@ public class JSONArray implements JSON, JSONGetter, List, Rando * @since 4.6.5 */ public JSONArray(Object object, JSONConfig jsonConfig) throws JSONException { - this(DEFAULT_CAPACITY, jsonConfig); - init(object); + this(object, jsonConfig, null); } - // -------------------------------------------------------------------------------------------------------------------- Constructor end + + /** + * 从对象构造
+ * 支持以下类型的参数: + * + *
+	 * 1. 数组
+	 * 2. {@link Iterable}对象
+	 * 3. JSON数组字符串
+	 * 
+ * + * @param object 数组或集合或JSON数组字符串 + * @param jsonConfig JSON选项 + * @param filter 键值对过滤编辑器,可以通过实现此接口,完成解析前对值的过滤和修改操作,{@code null}表示不过滤 + * @throws JSONException 非数组或集合 + * @since 5.8.0 + */ + public JSONArray(Object object, JSONConfig jsonConfig, Filter> filter) throws JSONException { + this(DEFAULT_CAPACITY, jsonConfig); + init(object, filter); + } + // endregion @Override public JSONConfig getConfig() { @@ -385,7 +361,7 @@ public class JSONArray implements JSON, JSONGetter, List, Rando @Override public boolean add(Object e) { - return addRaw(JSONUtil.wrap(e, this.config)); + return addRaw(JSONUtil.wrap(e, this.config), null); } @Override @@ -456,6 +432,28 @@ public class JSONArray implements JSON, JSONGetter, List, Rando */ @Override public Object set(int index, Object element) { + return set(index, element, null); + } + + /** + * 加入或者替换JSONArray中指定Index的值,如果index大于JSONArray的长度,将在指定index设置值,之前的位置填充JSONNull.Null + * + * @param index 位置 + * @param element 值对象. 可以是以下类型: Boolean, Double, Integer, JSONArray, JSONObject, Long, String, or the JSONNull.NULL. + * @param filter 过滤器,可以修改值,key(index)无法修改 + * @return 替换的值,即之前的值 + * @since 5.8.0 + */ + public Object set(int index, Object element, Filter> filter) { + // 添加前置过滤,通过MutablePair实现过滤、修改键值对等 + if (null != filter) { + final MutablePair pair = new MutablePair<>(index, element); + if (filter.accept(pair)) { + // 使用修改后的值 + element = pair.getValue(); + } + } + if (index >= size()) { add(index, element); } @@ -595,11 +593,23 @@ public class JSONArray implements JSON, JSONGetter, List, Rando /** * 原始添加,添加的对象不做任何处理 * - * @param obj 添加的对象 + * @param obj 添加的对象 + * @param filter 键值对过滤编辑器,可以通过实现此接口,完成解析前对值的过滤和修改操作,{@code null}表示不过滤 * @return 是否加入成功 * @since 5.8.0 */ - protected boolean addRaw(Object obj) { + protected boolean addRaw(Object obj, Filter> filter) { + // 添加前置过滤,通过MutablePair实现过滤、修改键值对等 + if (null != filter) { + final Mutable mutable = new MutableObj<>(obj); + if (filter.accept(mutable)) { + // 使用修改后的值 + obj = mutable.get(); + }else{ + // 键值对被过滤 + return false; + } + } return this.rawList.add(obj); } @@ -607,10 +617,11 @@ public class JSONArray implements JSON, JSONGetter, List, Rando * 初始化 * * @param source 数组或集合或JSON数组字符串 + * @param filter 键值对过滤编辑器,可以通过实现此接口,完成解析前对值的过滤和修改操作,{@code null}表示不过滤 * @throws JSONException 非数组或集合 */ @SuppressWarnings({"rawtypes", "unchecked"}) - private void init(Object source) throws JSONException { + private void init(Object source, Filter> filter) throws JSONException { if (null == source) { return; } @@ -621,9 +632,9 @@ public class JSONArray implements JSON, JSONGetter, List, Rando serializer.serialize(this, source); } else if (source instanceof CharSequence) { // JSON字符串 - init((CharSequence) source); + initFromStr((CharSequence) source, filter); } else if (source instanceof JSONTokener) { - init((JSONTokener) source); + initFromTokener((JSONTokener) source, filter); } else { Iterator iter; if (ArrayUtil.isArray(source)) {// 数组 @@ -641,7 +652,7 @@ public class JSONArray implements JSON, JSONGetter, List, Rando next = iter.next(); // 检查循环引用 if (next != source) { - this.add(next); + this.addRaw(JSONUtil.wrap(next, this.config), filter); } } } @@ -652,19 +663,20 @@ public class JSONArray implements JSON, JSONGetter, List, Rando * * @param source JSON字符串 */ - private void init(CharSequence source) { + private void initFromStr(CharSequence source, Filter> filter) { if (null != source) { - init(new JSONTokener(StrUtil.trim(source), this.config)); + initFromTokener(new JSONTokener(StrUtil.trim(source), this.config), filter); } } /** * 初始化 * - * @param x {@link JSONTokener} + * @param x {@link JSONTokener} + * @param filter 键值对过滤编辑器,可以通过实现此接口,完成解析前对值的过滤和修改操作,{@code null}表示不过滤 */ - private void init(JSONTokener x) { - JSONParser.of(x).parseTo(this); + 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 d3a3311ae..55d0e4693 100644 --- a/hutool-json/src/main/java/cn/hutool/json/JSONObject.java +++ b/hutool-json/src/main/java/cn/hutool/json/JSONObject.java @@ -6,9 +6,7 @@ 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.CaseInsensitiveLinkedMap; import cn.hutool.core.map.CaseInsensitiveMap; -import cn.hutool.core.map.CaseInsensitiveTreeMap; import cn.hutool.core.map.MapUtil; import cn.hutool.core.map.MapWrapper; import cn.hutool.core.util.ArrayUtil; @@ -26,12 +24,9 @@ import java.lang.reflect.Type; import java.math.BigDecimal; import java.math.BigInteger; import java.util.Collection; -import java.util.Comparator; import java.util.Enumeration; -import java.util.LinkedHashMap; import java.util.Map; import java.util.ResourceBundle; -import java.util.TreeMap; /** * JSON对象
@@ -119,8 +114,8 @@ public class JSONObject extends MapWrapper implements JSON, JSON * @since 4.1.19 */ public JSONObject(int capacity, JSONConfig config) { - super(createRaw(capacity, config)); - this.config = config; + super(InternalJSONUtil.createRawMap(capacity, ObjectUtil.defaultIfNull(config, JSONConfig.create()))); + this.config = ObjectUtil.defaultIfNull(config, JSONConfig.create()); } /** @@ -198,8 +193,30 @@ public class JSONObject extends MapWrapper implements JSON, JSON * @since 4.2.2 */ public JSONObject(Object source, JSONConfig config) { + this(source, config, null); + } + + /** + * 构建JSONObject,规则如下: + *
    + *
  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. + *
+ *

+ * 如果给定值为Map,将键值对加入JSON对象;
+ * 如果为普通的JavaBean,调用其getters方法(getXXX或者isXXX)获得值,加入到JSON对象
+ * 例如:如果JavaBean对象中有个方法getName(),值为"张三",获得的键值对为:name: "张三" + * + * @param source JavaBean或者Map对象或者String + * @param config JSON配置文件,{@code null}则使用默认配置 + * @param filter 键值对过滤编辑器,可以通过实现此接口,完成解析前对键值对的过滤和修改操作,{@code null}表示不过滤 + * @since 5.8.0 + */ + public JSONObject(Object source, JSONConfig config, Filter> filter) { this(DEFAULT_CAPACITY, config); - init(source); + init(source, filter); } /** @@ -219,7 +236,7 @@ public class JSONObject extends MapWrapper implements JSON, JSON public JSONObject(Object obj, String... names) { this(); if (ArrayUtil.isEmpty(names)) { - init(obj); + init(obj, null); return; } @@ -346,17 +363,47 @@ public class JSONObject extends MapWrapper implements JSON, JSON * @throws JSONException 值是无穷数字抛出此异常 */ public JSONObject set(String key, Object value) throws JSONException { + return set(key, value, null, false); + } + + /** + * 设置键值对到JSONObject中,在忽略null模式下,如果值为{@code null},将此键移除 + * + * @param key 键 + * @param value 值对象. 可以是以下类型: Boolean, Double, Integer, JSONArray, JSONObject, Long, String, or the JSONNull.NULL. + * @param filter 键值对过滤编辑器,可以通过实现此接口,完成解析前对键值对的过滤和修改操作,{@code null}表示不过滤 + * @return this. + * @throws JSONException 值是无穷数字抛出此异常 + * @since 5.8.0 + */ + public JSONObject set(String key, Object value, Filter> filter, boolean checkDuplicate) throws JSONException { if (null == key) { return this; } + // 添加前置过滤,通过MutablePair实现过滤、修改键值对等 + if (null != filter) { + final MutablePair pair = new MutablePair<>(key, value); + if (filter.accept(pair)) { + // 使用修改后的键值对 + key = pair.getKey(); + value = pair.getValue(); + }else{ + // 键值对被过滤 + return this; + } + } + final boolean ignoreNullValue = this.config.isIgnoreNullValue(); if (ObjectUtil.isNull(value) && ignoreNullValue) { // 忽略值模式下如果值为空清除key this.remove(key); } else { - InternalJSONUtil.testValidity(value); - super.put(key, JSONUtil.wrap(value, this.config)); + if(checkDuplicate && containsKey(key)){ + throw new JSONException("Duplicate key \"{}\"", key); + } + + super.put(key, JSONUtil.wrap(InternalJSONUtil.testValidity(value), this.config)); } return this; } @@ -370,13 +417,20 @@ public class JSONObject extends MapWrapper implements JSON, JSON * @throws JSONException 值是无穷数字、键重复抛出异常 */ public JSONObject putOnce(String key, Object value) throws JSONException { - if (key != null) { - if (containsKey(key)) { - throw new JSONException("Duplicate key \"{}\"", key); - } - this.set(key, value); - } - return this; + return setOnce(key, value, null); + } + + /** + * 一次性Put 键值对,如果key已经存在抛出异常,如果键值中有null值,忽略 + * + * @param key 键 + * @param value 值对象,可以是以下类型: Boolean, Double, Integer, JSONArray, JSONObject, Long, String, or the JSONNull.NULL. + * @return this. + * @throws JSONException 值是无穷数字、键重复抛出异常 + * @since 5.8.0 + */ + public JSONObject setOnce(String key, Object value, Filter> filter) throws JSONException { + return set(key, value, filter, true); } /** @@ -564,9 +618,10 @@ public class JSONObject extends MapWrapper implements JSON, JSON * * * @param source JavaBean或者Map对象或者String + * @param filter 键值对过滤编辑器,可以通过实现此接口,完成解析前对键值对的过滤和修改操作 */ @SuppressWarnings({"rawtypes", "unchecked"}) - private void init(Object source) { + private void init(Object source, Filter> filter) { if (null == source) { return; } @@ -586,22 +641,22 @@ public class JSONObject extends MapWrapper implements JSON, JSON if (source instanceof Map) { // Map for (final Entry e : ((Map) source).entrySet()) { - this.set(Convert.toStr(e.getKey()), e.getValue()); + 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()); + this.set(Convert.toStr(entry.getKey()), entry.getValue(), filter, false); } else if (source instanceof CharSequence) { // 可能为JSON字符串 - init((CharSequence) source); + initFromStr((CharSequence) source, filter); } else if (source instanceof JSONTokener) { // JSONTokener - init((JSONTokener) source); + initFromTokener((JSONTokener) source, filter); } else if (source instanceof ResourceBundle) { // JSONTokener - init((ResourceBundle) source); + initFromResourceBundle((ResourceBundle) source, filter); } else if (BeanUtil.isReadableBean(source.getClass())) { - // 普通Bean + // 普通Bean,过滤器无效 this.populateMap(source); } else { // 不支持对象类型转换为JSONObject @@ -613,14 +668,15 @@ public class JSONObject extends MapWrapper implements JSON, JSON * 初始化 * * @param bundle ResourceBundle + * @param filter 键值对过滤编辑器,可以通过实现此接口,完成解析前对键值对的过滤和修改操作,{@code null}表示不过滤 * @since 5.3.1 */ - private void init(ResourceBundle bundle) { + 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)); + InternalJSONUtil.propertyPut(this, key, bundle.getString(key), filter); } } } @@ -629,53 +685,26 @@ public class JSONObject extends MapWrapper implements JSON, JSON * 初始化,可以判断字符串为JSON或者XML * * @param source JSON字符串 + * @param filter 键值对过滤编辑器,可以通过实现此接口,完成解析前对键值对的过滤和修改操作,{@code null}表示不过滤 */ - private void init(CharSequence source) { + private void initFromStr(CharSequence source, Filter> filter) { final String jsonStr = StrUtil.trim(source); if (StrUtil.startWith(jsonStr, '<')) { // 可能为XML XML.toJSONObject(this, jsonStr, false); return; } - init(new JSONTokener(StrUtil.trim(source), this.config)); + initFromTokener(new JSONTokener(StrUtil.trim(source), this.config), filter); } /** * 初始化 * - * @param x JSONTokener + * @param x JSONTokener + * @param filter 键值对过滤编辑器,可以通过实现此接口,完成解析前对键值对的过滤和修改操作 */ - private void init(JSONTokener x) { - JSONParser.of(x).parseTo(this); - } - - /** - * 根据配置创建对应的原始Map - * - * @param capacity 初始大小 - * @param config JSON配置项,{@code null}则使用默认配置 - * @return Map - */ - private static Map createRaw(int capacity, JSONConfig config) { - Map rawHashMap; - if (null == config) { - config = JSONConfig.create(); - } - final Comparator keyComparator = config.getKeyComparator(); - if (config.isIgnoreCase()) { - if (null != keyComparator) { - rawHashMap = new CaseInsensitiveTreeMap<>(keyComparator); - } else { - rawHashMap = new CaseInsensitiveLinkedMap<>(capacity); - } - } else { - if (null != keyComparator) { - rawHashMap = new TreeMap<>(keyComparator); - } else { - rawHashMap = new LinkedHashMap<>(capacity); - } - } - return rawHashMap; + 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/JSONParser.java b/hutool-json/src/main/java/cn/hutool/json/JSONParser.java index 16db58bbb..8fd6b8388 100755 --- a/hutool-json/src/main/java/cn/hutool/json/JSONParser.java +++ b/hutool-json/src/main/java/cn/hutool/json/JSONParser.java @@ -1,5 +1,9 @@ package cn.hutool.json; +import cn.hutool.core.lang.Filter; +import cn.hutool.core.lang.mutable.Mutable; +import cn.hutool.core.lang.mutable.MutablePair; + /** * JSON字符串解析器 * @@ -7,7 +11,6 @@ package cn.hutool.json; * @since 5.8.0 */ public class JSONParser { - private final JSONTokener tokener; /** * 创建JSONParser @@ -19,6 +22,8 @@ public class JSONParser { return new JSONParser(tokener); } + private final JSONTokener tokener; + /** * 构造 * @@ -28,12 +33,14 @@ public class JSONParser { this.tokener = tokener; } + // region parseTo /** * 解析{@link JSONTokener}中的字符到目标的{@link JSONObject}中 * * @param jsonObject {@link JSONObject} + * @param filter 键值对过滤编辑器,可以通过实现此接口,完成解析前对键值对的过滤和修改操作,{@code null}表示不过滤 */ - public void parseTo(JSONObject jsonObject) { + public void parseTo(JSONObject jsonObject, Filter> filter) { final JSONTokener tokener = this.tokener; char c; @@ -60,7 +67,8 @@ public class JSONParser { if (c != ':') { throw tokener.syntaxError("Expected a ':' after a key"); } - jsonObject.putOnce(key, tokener.nextValue()); + + jsonObject.setOnce(key, tokener.nextValue(), filter); // Pairs are separated by ','. @@ -85,7 +93,7 @@ public class JSONParser { * * @param jsonArray {@link JSONArray} */ - public void parseTo(JSONArray jsonArray) { + public void parseTo(JSONArray jsonArray, Filter> filter) { final JSONTokener x = this.tokener; if (x.nextClean() != '[') { @@ -96,10 +104,10 @@ public class JSONParser { for (; ; ) { if (x.nextClean() == ',') { x.back(); - jsonArray.addRaw(JSONNull.NULL); + jsonArray.addRaw(JSONNull.NULL, filter); } else { x.back(); - jsonArray.addRaw(x.nextValue()); + jsonArray.addRaw(x.nextValue(), filter); } switch (x.nextClean()) { case ',': @@ -116,4 +124,5 @@ public class JSONParser { } } } + // endregion } diff --git a/hutool-json/src/test/java/cn/hutool/json/JSONArrayTest.java b/hutool-json/src/test/java/cn/hutool/json/JSONArrayTest.java index 75a2cd9f8..433755265 100644 --- a/hutool-json/src/test/java/cn/hutool/json/JSONArrayTest.java +++ b/hutool-json/src/test/java/cn/hutool/json/JSONArrayTest.java @@ -218,7 +218,7 @@ public class JSONArrayTest { } @Test - public void putTest(){ + public void putToIndexTest(){ final JSONArray jsonArray = new JSONArray(); jsonArray.put(3, "test"); // 第三个位置插入值,0~2都是null @@ -279,4 +279,30 @@ public class JSONArrayTest { Assert.assertEquals("[null]", array.toString()); } + + @Test + public void parseFilterTest() { + String jsonArr = "[{\"id\":111,\"name\":\"test1\"},{\"id\":112,\"name\":\"test2\"}]"; + //noinspection MismatchedQueryAndUpdateOfCollection + final JSONArray array = new JSONArray(jsonArr, null, (mutable) -> mutable.get().toString().contains("111")); + Assert.assertEquals(1, array.size()); + Assert.assertTrue(array.getJSONObject(0).containsKey("id")); + } + + @Test + public void parseFilterEditTest() { + String jsonArr = "[{\"id\":111,\"name\":\"test1\"},{\"id\":112,\"name\":\"test2\"}]"; + //noinspection MismatchedQueryAndUpdateOfCollection + final JSONArray array = new JSONArray(jsonArr, null, (mutable) -> { + final JSONObject o = new JSONObject(mutable.get()); + if("111".equals(o.getStr("id"))){ + o.set("name", "test1_edit"); + } + mutable.set(o); + return true; + }); + Assert.assertEquals(2, array.size()); + Assert.assertTrue(array.getJSONObject(0).containsKey("id")); + Assert.assertEquals("test1_edit", array.getJSONObject(0).get("name")); + } } 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 bd9440218..c39956d46 100644 --- a/hutool-json/src/test/java/cn/hutool/json/JSONObjectTest.java +++ b/hutool-json/src/test/java/cn/hutool/json/JSONObjectTest.java @@ -682,4 +682,26 @@ public class JSONObjectTest { }); Assert.assertEquals("{\"a\":\"\",\"b\":\"value2\"}", s); } + + @Test + public void parseFilterTest() { + String jsonStr = "{\"b\":\"value2\",\"c\":\"value3\",\"a\":\"value1\", \"d\": true, \"e\": null}"; + //noinspection MismatchedQueryAndUpdateOfCollection + JSONObject jsonObject = new JSONObject(jsonStr, null, (pair)-> "b".equals(pair.getKey())); + Assert.assertEquals(1, jsonObject.size()); + Assert.assertEquals("value2", jsonObject.get("b")); + } + + @Test + public void parseFilterEditTest() { + String jsonStr = "{\"b\":\"value2\",\"c\":\"value3\",\"a\":\"value1\", \"d\": true, \"e\": null}"; + //noinspection MismatchedQueryAndUpdateOfCollection + JSONObject jsonObject = new JSONObject(jsonStr, null, (pair)-> { + if("b".equals(pair.getKey())){ + pair.setValue(pair.getValue() + "_edit"); + } + return true; + }); + Assert.assertEquals("value2_edit", jsonObject.get("b")); + } }