From 0344982767b63975b2feafa0919a693919e99e89 Mon Sep 17 00:00:00 2001 From: Looly Date: Sun, 13 Feb 2022 22:51:58 +0800 Subject: [PATCH] add sort --- CHANGELOG.md | 1 + .../hutool/core/comparator/CompareUtil.java | 12 +++ .../core/map/CaseInsensitiveTreeMap.java | 73 +++++++++++++++++++ .../main/java/cn/hutool/json/JSONConfig.java | 41 +++++++++++ .../main/java/cn/hutool/json/JSONObject.java | 22 +++++- .../java/cn/hutool/json/IssueI4RBZ4Test.java | 18 +++++ ...ssues1881Test.java => Issues1881Test.java} | 9 ++- .../cn/hutool/json/JSONStrFormatterTest.java | 2 + .../java/cn/hutool/json/JSONUtilTest.java | 2 +- 9 files changed, 172 insertions(+), 8 deletions(-) create mode 100755 hutool-core/src/main/java/cn/hutool/core/map/CaseInsensitiveTreeMap.java create mode 100755 hutool-json/src/test/java/cn/hutool/json/IssueI4RBZ4Test.java rename hutool-json/src/test/java/cn/hutool/json/{issues1881Test.java => Issues1881Test.java} (74%) diff --git a/CHANGELOG.md b/CHANGELOG.md index dbb114a61..8beefb780 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ * 【core 】 DateUtil增加rangeContains、rangeNotContains(pr#537@Gitee) * 【core 】 Resource增加isModified默认方法 * 【core 】 增加VfsResource +* 【json 】 JSONConfig增加setKeyComparator、setNatureKeyComparator方法,支持自定义排序(issue#I4RBZ4@Gitee) ### 🐞Bug修复 * 【core 】 修复ChineseDate农历获取正月出现数组越界BUG(issue#2112@Github) diff --git a/hutool-core/src/main/java/cn/hutool/core/comparator/CompareUtil.java b/hutool-core/src/main/java/cn/hutool/core/comparator/CompareUtil.java index 4ff9948dd..d91516a09 100644 --- a/hutool-core/src/main/java/cn/hutool/core/comparator/CompareUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/comparator/CompareUtil.java @@ -11,6 +11,18 @@ import java.util.function.Function; */ public class CompareUtil { + /** + * 获取自然排序器,即默认排序器 + * + * @param 排序节点类型 + * @return 默认排序器 + * @since 5.7.21 + */ + @SuppressWarnings("unchecked") + public static > Comparator naturalComparator() { + return ComparableComparator.INSTANCE; + } + /** * 对象比较,比较结果取决于comparator,如果被比较对象为null,传入的comparator对象应处理此情况
* 如果传入comparator为null,则使用默认规则比较(此时被比较对象必须实现Comparable接口) diff --git a/hutool-core/src/main/java/cn/hutool/core/map/CaseInsensitiveTreeMap.java b/hutool-core/src/main/java/cn/hutool/core/map/CaseInsensitiveTreeMap.java new file mode 100755 index 000000000..7f8b065f6 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/map/CaseInsensitiveTreeMap.java @@ -0,0 +1,73 @@ +package cn.hutool.core.map; + +import java.util.Comparator; +import java.util.Map; +import java.util.SortedMap; +import java.util.TreeMap; + +/** + * 忽略大小写的{@link TreeMap}
+ * 对KEY忽略大小写,get("Value")和get("value")获得的值相同,put进入的值也会被覆盖 + * + * @author Looly + * + * @param 键类型 + * @param 值类型 + * @since 3.3.1 + */ +public class CaseInsensitiveTreeMap extends CustomKeyMap { + private static final long serialVersionUID = 4043263744224569870L; + + // ------------------------------------------------------------------------- Constructor start + /** + * 构造 + */ + public CaseInsensitiveTreeMap() { + this((Comparator) null); + } + + /** + * 构造 + * + * @param m Map + * @since 3.1.2 + */ + public CaseInsensitiveTreeMap(Map m) { + this(); + this.putAll(m); + } + + /** + * 构造 + * + * @param m Map + * @since 3.1.2 + */ + public CaseInsensitiveTreeMap(SortedMap m) { + super(new TreeMap(m)); + } + + /** + * 构造 + * + * @param comparator 比较器,{@code null}表示使用默认比较器 + */ + public CaseInsensitiveTreeMap(Comparator comparator) { + super(new TreeMap<>(comparator)); + } + // ------------------------------------------------------------------------- Constructor end + + /** + * 将Key转为小写 + * + * @param key KEY + * @return 小写KEY + */ + @Override + protected Object customKey(Object key) { + if (key instanceof CharSequence) { + key = key.toString().toLowerCase(); + } + return key; + } +} diff --git a/hutool-json/src/main/java/cn/hutool/json/JSONConfig.java b/hutool-json/src/main/java/cn/hutool/json/JSONConfig.java index cf36614e7..2d9279c1c 100644 --- a/hutool-json/src/main/java/cn/hutool/json/JSONConfig.java +++ b/hutool-json/src/main/java/cn/hutool/json/JSONConfig.java @@ -1,6 +1,9 @@ package cn.hutool.json; +import cn.hutool.core.comparator.CompareUtil; + import java.io.Serializable; +import java.util.Comparator; /** * JSON配置项 @@ -15,6 +18,10 @@ public class JSONConfig implements Serializable { * 是否有序,顺序按照加入顺序排序,只针对JSONObject有效 */ private boolean order; + /** + * 键排序规则,{@code null}表示不排序,不排序情况下,如果{@link #order}为{@code true}按照加入顺序排序,否则按照hash排序 + */ + private Comparator keyComparator; /** * 是否忽略转换过程中的异常 */ @@ -70,6 +77,40 @@ public class JSONConfig implements Serializable { return this; } + /** + * 获取键排序规则
+ * 键排序规则,{@code null}表示不排序,不排序情况下,如果{@link #order}为{@code true}按照加入顺序排序,否则按照hash排序 + * + * @return 键排序规则 + * @since 5.7.21 + */ + public Comparator getKeyComparator() { + return this.keyComparator; + } + + /** + * 设置自然排序,即按照字母顺序排序 + * + * @return this + * @since 5.7.21 + */ + public JSONConfig setNatureKeyComparator() { + return setKeyComparator(CompareUtil.naturalComparator()); + } + + /** + * 设置键排序规则
+ * 键排序规则,{@code null}表示不排序,不排序情况下,如果{@link #order}为{@code true}按照加入顺序排序,否则按照hash排序 + * + * @param keyComparator 键排序规则 + * @return this + * @since 5.7.21 + */ + public JSONConfig setKeyComparator(Comparator keyComparator) { + this.keyComparator = keyComparator; + return this; + } + /** * 是否忽略转换过程中的异常 * 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 6442d84c7..f833a1349 100644 --- a/hutool-json/src/main/java/cn/hutool/json/JSONObject.java +++ b/hutool-json/src/main/java/cn/hutool/json/JSONObject.java @@ -10,6 +10,7 @@ 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.util.ArrayUtil; import cn.hutool.core.util.ObjectUtil; @@ -25,10 +26,12 @@ import java.io.Writer; import java.math.BigDecimal; import java.math.BigInteger; import java.util.Collection; +import java.util.Comparator; import java.util.Enumeration; import java.util.Map; import java.util.ResourceBundle; import java.util.Set; +import java.util.TreeMap; /** * JSON对象
@@ -121,9 +124,21 @@ public class JSONObject implements JSON, JSONGetter, Map config = JSONConfig.create(); } if (config.isIgnoreCase()) { - this.rawHashMap = config.isOrder() ? new CaseInsensitiveLinkedMap<>(capacity) : new CaseInsensitiveMap<>(capacity); + final Comparator keyComparator = config.getKeyComparator(); + if(null != keyComparator){ + // 比较器存在情况下,isOrder无效 + this.rawHashMap = new CaseInsensitiveTreeMap<>(keyComparator); + }else{ + this.rawHashMap = config.isOrder() ? new CaseInsensitiveLinkedMap<>(capacity) : new CaseInsensitiveMap<>(capacity); + } } else { - this.rawHashMap = MapUtil.newHashMap(capacity, config.isOrder()); + final Comparator keyComparator = config.getKeyComparator(); + if(null != keyComparator){ + // 比较器存在情况下,isOrder无效 + this.rawHashMap = new TreeMap<>(keyComparator); + }else{ + this.rawHashMap = MapUtil.newHashMap(capacity, config.isOrder()); + } } this.config = config; } @@ -178,7 +193,8 @@ public class JSONObject implements JSON, JSONGetter, Map public JSONObject(Object source, boolean ignoreNullValue, boolean isOrder) { this(source, JSONConfig.create().setOrder(isOrder)// .setIgnoreCase((source instanceof CaseInsensitiveMap))// - .setIgnoreNullValue(ignoreNullValue)); + .setIgnoreNullValue(ignoreNullValue) + ); } /** diff --git a/hutool-json/src/test/java/cn/hutool/json/IssueI4RBZ4Test.java b/hutool-json/src/test/java/cn/hutool/json/IssueI4RBZ4Test.java new file mode 100755 index 000000000..021fd3333 --- /dev/null +++ b/hutool-json/src/test/java/cn/hutool/json/IssueI4RBZ4Test.java @@ -0,0 +1,18 @@ +package cn.hutool.json; + +import org.junit.Assert; +import org.junit.Test; + +/** + * https://gitee.com/dromara/hutool/issues/I4RBZ4 + */ +public class IssueI4RBZ4Test { + + @Test + public void sortTest(){ + String jsonStr = "{\"id\":\"123\",\"array\":[1,2,3],\"outNum\":356,\"body\":{\"ava1\":\"20220108\",\"use\":1,\"ava2\":\"20230108\"},\"name\":\"John\"}"; + + final JSONObject jsonObject = JSONUtil.parseObj(jsonStr, JSONConfig.create().setNatureKeyComparator()); + Assert.assertEquals("{\"array\":[1,2,3],\"body\":{\"ava1\":\"20220108\",\"ava2\":\"20230108\",\"use\":1},\"id\":\"123\",\"name\":\"John\",\"outNum\":356}", jsonObject.toString()); + } +} diff --git a/hutool-json/src/test/java/cn/hutool/json/issues1881Test.java b/hutool-json/src/test/java/cn/hutool/json/Issues1881Test.java similarity index 74% rename from hutool-json/src/test/java/cn/hutool/json/issues1881Test.java rename to hutool-json/src/test/java/cn/hutool/json/Issues1881Test.java index 07cdee890..a8475bcff 100644 --- a/hutool-json/src/test/java/cn/hutool/json/issues1881Test.java +++ b/hutool-json/src/test/java/cn/hutool/json/Issues1881Test.java @@ -1,18 +1,19 @@ package cn.hutool.json; -import cn.hutool.core.lang.Console; import lombok.Data; import lombok.experimental.Accessors; +import org.junit.Assert; import org.junit.Test; import java.io.Serializable; import java.util.ArrayList; import java.util.List; -public class issues1881Test { +public class Issues1881Test { + @Accessors(chain = true) @Data - public class ThingsHolderContactVO implements Serializable { + static class ThingsHolderContactVO implements Serializable { private static final long serialVersionUID = -8727337936070932370L; private Long id; @@ -30,6 +31,6 @@ public class issues1881Test { holderContactVOList.add(new ThingsHolderContactVO().setId(1L).setName("1")); holderContactVOList.add(new ThingsHolderContactVO().setId(2L).setName("2")); - Console.log(JSONUtil.parseArray(holderContactVOList)); + Assert.assertEquals("[{\"name\":\"1\",\"id\":1},{\"name\":\"2\",\"id\":2}]", JSONUtil.parseArray(holderContactVOList).toString()); } } diff --git a/hutool-json/src/test/java/cn/hutool/json/JSONStrFormatterTest.java b/hutool-json/src/test/java/cn/hutool/json/JSONStrFormatterTest.java index fc89eace0..9a6ae58e0 100644 --- a/hutool-json/src/test/java/cn/hutool/json/JSONStrFormatterTest.java +++ b/hutool-json/src/test/java/cn/hutool/json/JSONStrFormatterTest.java @@ -2,6 +2,7 @@ package cn.hutool.json; import cn.hutool.core.lang.Console; import org.junit.Assert; +import org.junit.Ignore; import org.junit.Test; /** @@ -33,6 +34,7 @@ public class JSONStrFormatterTest { } @Test + @Ignore public void formatTest4(){ String jsonStr = "{\"employees\":[{\"firstName\":\"Bill\",\"lastName\":\"Gates\"},{\"firstName\":\"George\",\"lastName\":\"Bush\"},{\"firstName\":\"Thomas\",\"lastName\":\"Carter\"}]}"; Console.log(JSONUtil.formatJsonStr(jsonStr)); 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 a067554e0..8a99de73d 100644 --- a/hutool-json/src/test/java/cn/hutool/json/JSONUtilTest.java +++ b/hutool-json/src/test/java/cn/hutool/json/JSONUtilTest.java @@ -36,7 +36,7 @@ public class JSONUtilTest { @Test(expected = JSONException.class) public void parseNumberTest() { JSONArray json = JSONUtil.parseArray(123L); - Console.log(json); + Assert.assertNotNull(json); } /**