diff --git a/hutool-core/src/main/java/cn/hutool/core/bean/BeanPath.java b/hutool-core/src/main/java/cn/hutool/core/bean/BeanPath.java index d7f1db7f5a53f36af571f36e2edc0b3cc37d896b..5962194483557ac312815855c7699c38c0653011 100644 --- a/hutool-core/src/main/java/cn/hutool/core/bean/BeanPath.java +++ b/hutool-core/src/main/java/cn/hutool/core/bean/BeanPath.java @@ -192,6 +192,17 @@ public class BeanPath implements Serializable { String patternPart; for (int i = 0; i < length; i++) { patternPart = patternParts.get(i); + + // 支持通配符 * 语法,用于获取集合或数组中所有元素的指定字段 + if ("*".equals(patternPart)) { + // 如果当前对象是集合或数组,且后面还有路径部分,则展开处理 + if (i < length - 1 && (subBean instanceof Collection || ArrayUtil.isArray(subBean))) { + return getByWildcard(subBean, patternParts, i + 1, length); + } + // 如果 * 是最后一个部分,直接返回当前集合/数组 + return subBean; + } + subBean = getFieldValue(subBean, patternPart); if (null == subBean) { // 支持表达式的第一个对象为Bean本身(若用户定义表达式$开头,则不做此操作) @@ -206,6 +217,49 @@ public class BeanPath implements Serializable { return subBean; } + /** + * 处理通配符 * 的情况,对集合或数组中的每个元素应用剩余路径 + * + * @param collectionOrArray 集合或数组对象 + * @param patternParts 完整的表达式分段列表 + * @param startIndex 剩余路径的起始索引 + * @param endIndex 路径的结束索引 + * @return 结果列表 + */ + private Object getByWildcard(final Object collectionOrArray, final List patternParts, + final int startIndex, final int endIndex) { + final List results = new ArrayList<>(); + + // 获取剩余的路径部分 + final List remainingParts = patternParts.subList(startIndex, endIndex); + + if (collectionOrArray instanceof Collection) { + // 处理集合 + for (Object item : (Collection) collectionOrArray) { + if (item != null) { + final Object value = get(remainingParts, item, false); + if (value != null) { + results.add(value); + } + } + } + } else if (ArrayUtil.isArray(collectionOrArray)) { + // 处理数组 + final int arrayLength = ArrayUtil.length(collectionOrArray); + for (int i = 0; i < arrayLength; i++) { + final Object item = ArrayUtil.get(collectionOrArray, i); + if (item != null) { + final Object value = get(remainingParts, item, false); + if (value != null) { + results.add(value); + } + } + } + } + + return results.isEmpty() ? null : results; + } + @SuppressWarnings("unchecked") private static Object getFieldValue(final Object bean, final String expression) { if (StrUtil.isBlank(expression)) { diff --git a/hutool-core/src/test/java/cn/hutool/core/bean/BeanPathTest.java b/hutool-core/src/test/java/cn/hutool/core/bean/BeanPathTest.java index dfb866de3466950f81079f80b389109b0f16d1e3..a6580d1ce732308acee3946cf9fd655eeaa44289 100644 --- a/hutool-core/src/test/java/cn/hutool/core/bean/BeanPathTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/bean/BeanPathTest.java @@ -139,6 +139,16 @@ public class BeanPathTest { assertEquals("[LOL, KFC, COFFE]", ArrayUtil.toString(myUser.getHobby())); } + @Test + public void wildcardTest() { + // 测试通配符 * 语法 + final BeanPath pattern = BeanPath.create("userInfo.examInfoDict[*].id"); + final Object result = pattern.get(tempMap); + + // 应该返回一个包含所有 examInfoDict 元素的 id 的列表 + assertEquals("[1, 2, 3]", result.toString()); + } + @Data static class MyUser { private String[] hobby; diff --git a/hutool-json/src/test/java/cn/hutool/json/JSONPathTest.java b/hutool-json/src/test/java/cn/hutool/json/JSONPathTest.java index 437f012cb5d58f640bb2663e1aba1eacfb5da41f..92c0f6e2c48118fd68ee23c7be06b9f968a4d05b 100644 --- a/hutool-json/src/test/java/cn/hutool/json/JSONPathTest.java +++ b/hutool-json/src/test/java/cn/hutool/json/JSONPathTest.java @@ -50,4 +50,27 @@ public class JSONPathTest { assertNotNull(accountIds); assertArrayEquals(new Long[]{1L, 2L, 3L}, accountIds.toArray()); } + + @Test + public void getByPathWithWildcardTest() { + // 测试通配符 [*] 语法 + JSONObject root = new JSONObject() + .put("actionMessage", new JSONObject() + .put("alertResults", new JSONArray()) + .put("decodeFeas", new JSONArray() + .put(new JSONObject() + .put("body", new JSONObject() + .put("lats", new JSONArray() + .put(new JSONObject().put("begin", 4260).put("text", "呵呵")) + .put(new JSONObject().put("begin", 4260).put("text", "你好 ")) + ) + ) + ) + ) + ); + + Object byPath = JSONUtil.getByPath(root, "$.actionMessage.decodeFeas[0].body.lats[*].text"); + assertNotNull(byPath); + assertEquals("[呵呵, 你好 ]", byPath.toString()); + } }