implements JSON, JSON
* @since 4.2.2
*/
public JSONObject(Object source, JSONConfig config) {
+ this(source, config, null);
+ }
+
+ /**
+ * 构建JSONObject,规则如下:
+ *
+ * - value为Map,将键值对加入JSON对象
+ * - value为JSON字符串(CharSequence),使用JSONTokener解析
+ * - value为JSONTokener,直接解析
+ * - value为普通JavaBean,如果为普通的JavaBean,调用其getters方法(getXXX或者isXXX)获得值,加入到JSON对象。例如:如果JavaBean对象中有个方法getName(),值为"张三",获得的键值对为:name: "张三"
+ *
+ *
+ * 如果给定值为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"));
+ }
}