Created
December 18, 2025 07:05
-
-
Save HydrangeaPurple/f3e3f9b3108febb6ac6551f04c88b8af to your computer and use it in GitHub Desktop.
java Map List 对象模板匹配拼装
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package util; | |
| import com.alibaba.fastjson2.JSON; | |
| import util.ObjectIsNull; | |
| import util.ObjectPathUtil.Rule; | |
| import lombok.AllArgsConstructor; | |
| import lombok.Data; | |
| import org.mvel2.MVEL; | |
| import java.util.*; | |
| /** | |
| * 用来判断全局对象中是否满足了inOutEntity的定义 | |
| * | |
| * @author cyq | |
| * @version V1.0 | |
| * @since V1.0 | |
| */ | |
| @SuppressWarnings("unchecked") | |
| public class InOutEntityUtil { | |
| /** | |
| * 使用全局实例对象context, 根据样板对象inEntity, 来构建一个层级和key相同, value来自context的新map | |
| * | |
| * @param inEntity 样板对象(当有子结构时, 只考虑Map子结构, 不考虑List子结构) | |
| * @param context 全局上下文对象 | |
| * @return 根据样板对象构建出来的新的对象, 新对象的Value只考虑Map和String | |
| */ | |
| public static EntityMatch getEntityFromContext(String inEntity, Map<String, Object> context) { | |
| if (ObjectIsNull.check(inEntity)) { | |
| return new EntityMatch(false, new HashMap<>()); | |
| } | |
| Map<String, Object> inEntityMap = JSON.parseObject(inEntity, Map.class); | |
| List<Rule> rules = WalkMapUtil.walkObj(inEntityMap); | |
| boolean match = true; | |
| Map<String, Object> result = new HashMap<>(); | |
| for (Rule rule : rules) { | |
| Object exeValue = MVEL.eval(rule.mvelExpr(), context); | |
| if (ObjectIsNull.check(exeValue)) { | |
| match = false; | |
| } | |
| result = (Map<String, Object>) ObjectPathUtil.putValue( | |
| result, | |
| ObjectPathUtil.parsePath(rule.targetPath()), | |
| exeValue | |
| ); | |
| } | |
| return new EntityMatch(match, result); | |
| } | |
| /** | |
| * @param outEntity 样板对象 | |
| * @param context 全局上下文对象 | |
| * @return | |
| */ | |
| @SuppressWarnings({"unchecked", "rawtypes"}) | |
| public static Map<String, Object> extractOutEntity(String outEntity, Map<String, Object> context) { | |
| Map<String, Object> result = new HashMap<>(); | |
| Map<String, Object> outEntityMap = JSON.parseObject(outEntity, Map.class); | |
| List<Rule> rules = WalkMapUtil.walkObj(outEntityMap); | |
| for (Rule rule : rules) { | |
| Object exeValue = MVEL.eval(rule.mvelExpr(), context); | |
| result = (Map<String, Object>) ObjectPathUtil.putValue( | |
| result, | |
| ObjectPathUtil.parsePath(rule.targetPath()), | |
| exeValue | |
| ); | |
| } | |
| return result; | |
| } | |
| public static void main1(String[] args) { | |
| // 测试案例1: 简单的键值对匹配 | |
| System.out.println("=== 测试案例1: 简单的键值对匹配 ==="); | |
| Map<String, Object> template1 = new HashMap<>(); | |
| template1.put("name", "string"); | |
| template1.put("age", "int"); | |
| Map<String, Object> context1 = new HashMap<>(); | |
| context1.put("name", "张三"); | |
| context1.put("age", "25"); | |
| context1.put("extra", "额外信息"); | |
| EntityMatch result1 = InOutEntityUtil.getEntityFromContext(JSON.toJSONString(template1), context1); | |
| System.out.println("原始数据: " + context1); | |
| System.out.println("模板: " + template1); | |
| System.out.println("匹配结果: " + result1.isMatch()); | |
| System.out.println("提取数据: " + result1.getEntityFromContext()); | |
| // 测试案例2: 嵌套结构匹配 | |
| System.out.println("\n=== 测试案例2: 嵌套结构匹配 ==="); | |
| Map<String, Object> addressTemplate = new HashMap<>(); | |
| addressTemplate.put("street", "string"); | |
| addressTemplate.put("city", "string"); | |
| Map<String, Object> template2 = new HashMap<>(); | |
| template2.put("name", "string"); | |
| // template2.put("address", addressTemplate); | |
| template2.put("address", "string"); | |
| Map<String, Object> addressContext = new HashMap<>(); | |
| addressContext.put("street", "中山路123号"); | |
| addressContext.put("city", "北京"); | |
| addressContext.put("zipcode", "100000"); | |
| Map<String, Object> context2 = new HashMap<>(); | |
| context2.put("name", "李四"); | |
| context2.put("address", addressContext); | |
| context2.put("phone", "13800138000"); | |
| EntityMatch result2 = InOutEntityUtil.getEntityFromContext(JSON.toJSONString(template2), context2); | |
| System.out.println("原始数据: " + context2); | |
| System.out.println("模板: " + template2); | |
| System.out.println("匹配结果: " + result2.isMatch()); | |
| System.out.println("提取数据: " + result2.getEntityFromContext()); | |
| // 测试案例3: 模板中有但上下文中没有的键 | |
| System.out.println("\n=== 测试案例3: 模板中有但上下文中没有的键 ==="); | |
| Map<String, Object> template3 = new HashMap<>(); | |
| template3.put("name", "string"); | |
| template3.put("email", "string"); // 上下文中没有email | |
| Map<String, Object> context3 = new HashMap<>(); | |
| context3.put("name", "王五"); | |
| context3.put("phone", "13900139000"); | |
| EntityMatch result3 = InOutEntityUtil.getEntityFromContext(JSON.toJSONString(template3), context3); | |
| System.out.println("原始数据: " + context3); | |
| System.out.println("模板: " + template3); | |
| System.out.println("匹配结果: " + result3.isMatch()); | |
| System.out.println("提取数据: " + result3.getEntityFromContext()); | |
| // 测试案例4: 空模板 | |
| System.out.println("\n=== 测试案例4: 空模板 ==="); | |
| Map<String, Object> template4 = new HashMap<>(); | |
| Map<String, Object> context4 = new HashMap<>(); | |
| context4.put("name", "赵六"); | |
| EntityMatch result4 = InOutEntityUtil.getEntityFromContext(JSON.toJSONString(template4), context4); | |
| System.out.println("原始数据: " + context4); | |
| System.out.println("模板: " + template4); | |
| System.out.println("匹配结果: " + result4.isMatch()); | |
| System.out.println("提取数据: " + result4.getEntityFromContext()); | |
| // 测试案例5: List类型匹配 | |
| System.out.println("\n=== 测试案例5: List类型匹配 ==="); | |
| Map<String, Object> template5 = new HashMap<>(); | |
| template5.put("name", "string"); | |
| template5.put("hobbies", "list"); // List类型 | |
| Map<String, Object> context5 = new HashMap<>(); | |
| context5.put("name", "孙七"); | |
| context5.put("hobbies", java.util.List.of("读书", "游泳", "跑步")); | |
| context5.put("age", 30); | |
| EntityMatch result5 = InOutEntityUtil.getEntityFromContext(JSON.toJSONString(template5), context5); | |
| System.out.println("原始数据: " + context5); | |
| System.out.println("模板: " + template5); | |
| System.out.println("匹配结果: " + result5.isMatch()); | |
| System.out.println("提取数据: " + result5.getEntityFromContext()); | |
| } | |
| public static void main2(String[] args) { | |
| String expr = """ | |
| [ | |
| "order": [ | |
| "createStaff": staff.createStaff, | |
| "createOrgId": staff.createOrgId, | |
| "latnId": latnId, | |
| "goods": goods, | |
| "cust": custInfo | |
| ] | |
| ]"""; | |
| Map<String, Object> env = new HashMap<>(); | |
| Map<String, Object> staff = new HashMap<>(); | |
| staff.put("createStaff", "10001"); | |
| staff.put("createOrgId", "20001"); | |
| env.put("staff", staff); | |
| env.put("latnId", "3301"); | |
| env.put("goods", Collections.emptyList()); // List / Map 均可 | |
| env.put("custInfo", Collections.emptyMap()); // Map | |
| Object result = MVEL.eval(expr, env); | |
| Map<String, Object> resultMap = (Map<String, Object>) result; | |
| System.out.println(resultMap); | |
| } | |
| public static void main3(String[] args) { | |
| // 构造一个复杂的嵌套结构进行测试 | |
| Map<String, Object> complexMap = new HashMap<>(); | |
| // 添加简单键值对 | |
| complexMap.put("name", "张三"); | |
| complexMap.put("age", 25); | |
| // 添加嵌套Map | |
| Map<String, Object> address = new HashMap<>(); | |
| address.put("street", "中山路123号"); | |
| address.put("city", "北京"); | |
| complexMap.put("address", address); | |
| // 添加嵌套List | |
| List<String> hobbies = Arrays.asList("读书", "游泳", "跑步"); | |
| complexMap.put("hobbies", hobbies); | |
| // 添加嵌套的复杂List(包含Map元素) | |
| List<Map<String, Object>> contacts = new ArrayList<>(); | |
| Map<String, Object> contact1 = new HashMap<>(); | |
| contact1.put("type", "手机"); | |
| contact1.put("number", "13311111111"); | |
| contacts.add(contact1); | |
| Map<String, Object> contact2 = new HashMap<>(); | |
| contact2.put("type", "邮箱"); | |
| contact2.put("address", "zhangsan@example.com"); | |
| contacts.add(contact2); | |
| complexMap.put("contacts", contacts); | |
| System.out.println(complexMap); | |
| // 创建InOutEntityUtil实例并调用walk方法 | |
| System.out.println("遍历复杂嵌套结构:"); | |
| List<Rule> rules = WalkMapUtil.walkObj(complexMap); | |
| System.out.println(JSON.toJSONString(rules)); | |
| Map<String, Object> outPut = new HashMap<>(); | |
| for (Rule rule : rules) { | |
| outPut = (Map<String, Object>) ObjectPathUtil.putValue( | |
| outPut, | |
| ObjectPathUtil.parsePath(rule.targetPath()), | |
| MVEL.eval(rule.mvelExpr(), complexMap) | |
| ); | |
| } | |
| System.out.println("生成的新对象:"); | |
| System.out.println(outPut); | |
| } | |
| public static void main(String[] args) { | |
| String inEntity = "{\"event\":\"\",\"action\":\"\",\"goodsIntent\":\"list\", \"custId\": \"#cust.custId\"}"; | |
| Map<String, Object> context = new HashMap<>(); | |
| // 构造符合inEntity报文的上下文数据 | |
| context.put("event", "login"); | |
| context.put("action", "authenticate"); | |
| // 构造goodsIntent列表数据 | |
| List<Map<String, Object>> goodsIntent = new ArrayList<>(); | |
| Map<String, Object> goods1 = new HashMap<>(); | |
| goods1.put("id", "goods001"); | |
| goods1.put("name", "商品1"); | |
| goods1.put("price", 100); | |
| goodsIntent.add(goods1); | |
| Map<String, Object> goods2 = new HashMap<>(); | |
| goods2.put("id", "goods002"); | |
| goods2.put("name", "商品2"); | |
| goods2.put("price", 200); | |
| goodsIntent.add(goods2); | |
| context.put("goodsIntent", goodsIntent); | |
| // 构造cust信息 | |
| Map<String, Object> cust = new HashMap<>(); | |
| cust.put("custId", "cust12345"); | |
| context.put("cust", cust); | |
| // 调用getEntityFromContext方法 | |
| EntityMatch result = InOutEntityUtil.getEntityFromContext(inEntity, context); | |
| System.out.println("输入的inEntity: " + inEntity); | |
| System.out.println("构造的上下文: " + context); | |
| System.out.println("匹配结果: " + result.isMatch()); | |
| System.out.println("提取的数据: " + result.getEntityFromContext()); | |
| } | |
| @Data | |
| @AllArgsConstructor | |
| public static class EntityMatch { | |
| boolean match; | |
| Map<String, Object> entityFromContext; | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package util; | |
| import java.util.ArrayList; | |
| import java.util.HashMap; | |
| import java.util.List; | |
| import java.util.Map; | |
| /** | |
| * [简要描述类的用途] | |
| * | |
| * <p>[更详细的说明,可以包括背景信息、用途等]</p> | |
| * | |
| * <p>注释中的段落使用 HTML 标签包裹,例如 `<p>` 或 `<ul>`。</p> | |
| * | |
| * @author cyq | |
| * @version V1.0 | |
| * @since V1.0 | |
| */ | |
| public class ObjectPathUtil { | |
| public record Rule(String targetPath, String mvelExpr) {} | |
| public sealed interface PathSeg permits KeySeg, IndexSeg {} | |
| public record KeySeg(String key) implements PathSeg {} | |
| public record IndexSeg(int index) implements PathSeg {} | |
| public static List<PathSeg> parsePath(String path) { | |
| List<PathSeg> result = new ArrayList<>(); | |
| int i = 0; | |
| while (i < path.length()) { | |
| if (path.charAt(i) == '.') { | |
| i++; | |
| continue; | |
| } | |
| if (path.charAt(i) == '[') { | |
| int end = path.indexOf(']', i); | |
| int index = Integer.parseInt(path.substring(i + 1, end)); | |
| result.add(new IndexSeg(index)); | |
| i = end + 1; | |
| } else { | |
| int start = i; | |
| while (i < path.length() | |
| && path.charAt(i) != '.' | |
| && path.charAt(i) != '[') { | |
| i++; | |
| } | |
| result.add(new KeySeg(path.substring(start, i))); | |
| } | |
| } | |
| return result; | |
| } | |
| public static Object putValue(Object root, List<PathSeg> path, Object value) { | |
| if (root == null) { | |
| root = (path.get(0) instanceof IndexSeg) ? new ArrayList<>() : new HashMap<>(); | |
| } | |
| Object current = root; | |
| for (int i = 0; i < path.size(); i++) { | |
| PathSeg seg = path.get(i); | |
| boolean last = (i == path.size() - 1); | |
| if (seg instanceof KeySeg keySeg) { | |
| Map<String, Object> map = castMap(current); | |
| if (last) { | |
| map.put(keySeg.key(), value); | |
| } else { | |
| Object next = map.get(keySeg.key()); | |
| if (next == null) { | |
| next = createNext(path.get(i + 1)); | |
| map.put(keySeg.key(), next); | |
| } | |
| current = next; | |
| } | |
| } | |
| if (seg instanceof IndexSeg indexSeg) { | |
| List<Object> list = castList(current); | |
| int idx = indexSeg.index(); | |
| while (list.size() <= idx) { | |
| list.add(null); | |
| } | |
| if (last) { | |
| list.set(idx, value); | |
| } else { | |
| Object next = list.get(idx); | |
| if (next == null) { | |
| next = createNext(path.get(i + 1)); | |
| list.set(idx, next); | |
| } | |
| current = next; | |
| } | |
| } | |
| } | |
| return root; | |
| } | |
| private static Map<String, Object> castMap(Object o) { | |
| if (o instanceof Map) return (Map<String, Object>) o; | |
| throw new IllegalStateException("Expected Map but got " + o); | |
| } | |
| private static List<Object> castList(Object o) { | |
| if (o instanceof List) return (List<Object>) o; | |
| throw new IllegalStateException("Expected List but got " + o); | |
| } | |
| private static Object createNext(PathSeg nextSeg) { | |
| return (nextSeg instanceof IndexSeg) ? new ArrayList<>() : new HashMap<>(); | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package util; | |
| import java.util.*; | |
| /** | |
| * 遍历map得到结构 | |
| * | |
| * @author cyq | |
| * @version V1.0 | |
| * @since V1.0 | |
| */ | |
| public class WalkMapUtil { | |
| public static List<ObjectPathUtil.Rule> walkObj(Object node) { | |
| List<ObjectPathUtil.Rule> rules = new ArrayList<>(); | |
| walk(node, new ArrayDeque<>(), rules); | |
| return rules; | |
| } | |
| /** | |
| * 遍历map得到结构 | |
| * @param node 原始参考对象 | |
| * @param path 临时保存路径的双端队列, 固定传 new ArrayDeque<>() | |
| * @param rules 返回的对象结构清单 | |
| */ | |
| private static void walk(Object node, Deque<String> path, List<ObjectPathUtil.Rule> rules) { | |
| if (node instanceof Map) { | |
| walkMap((Map<?, ?>) node, path, rules); | |
| } else if (node instanceof List) { | |
| walkList((List<?>) node, path, rules); | |
| } else { | |
| String buildPath = buildPath(path); | |
| // 叶子节点(String / Number / Boolean / null 等) | |
| String strNode = Objects.toString(node, "").trim(); | |
| String varName = extractVariableName(strNode); | |
| if (varName != null) { | |
| rules.add(new ObjectPathUtil.Rule(buildPath, varName)); | |
| } else { | |
| rules.add(new ObjectPathUtil.Rule(buildPath, buildPath)); | |
| } | |
| } | |
| } | |
| private static void walkMap(Map<?, ?> map, Deque<String> path, List<ObjectPathUtil.Rule> rules) { | |
| for (Map.Entry<?, ?> entry : map.entrySet()) { | |
| String key = String.valueOf(entry.getKey()); | |
| path.addLast("." + key); // 进入 | |
| walk(entry.getValue(), path, rules); | |
| path.removeLast(); // 退出 | |
| } | |
| } | |
| private static void walkList(List<?> list, Deque<String> path, List<ObjectPathUtil.Rule> rules) { | |
| for (int i = 0; i < list.size(); i++) { | |
| path.addLast("[" + i + "]"); | |
| walk(list.get(i), path, rules); | |
| path.removeLast(); | |
| } | |
| } | |
| private static String buildPath(Deque<String> path) { | |
| if (path.isEmpty()) return ""; | |
| StringBuilder sb = new StringBuilder(); | |
| for (String p : path) { | |
| sb.append(p); | |
| } | |
| // 去掉第一个点 | |
| return sb.charAt(0) == '.' ? sb.substring(1) : sb.toString(); | |
| } | |
| private static String extractVariableName(String str) { | |
| if (str.startsWith("#{") && str.endsWith("}")) { | |
| // 匹配 #{varName} 格式 | |
| return str.substring(2, str.length() - 1); | |
| } else if (str.startsWith("${") && str.endsWith("}")) { | |
| // 匹配 ${varName} 格式 | |
| return str.substring(2, str.length() - 1); | |
| } else if (str.startsWith("{") && str.endsWith("}")) { | |
| // 匹配 {varName} 格式 | |
| return str.substring(1, str.length() - 1); | |
| } else if (str.startsWith("[") && str.endsWith("]")) { | |
| // 匹配 [varName] 格式 | |
| return str.substring(1, str.length() - 1); | |
| } else if (str.startsWith("#") && str.endsWith("#")) { | |
| // 匹配 #varName# 格式 | |
| return str.substring(1, str.length() - 1); | |
| } else if (str.startsWith("$") && str.endsWith("$")) { | |
| // 匹配 $varName$ 格式 | |
| return str.substring(1, str.length() - 1); | |
| } else if (str.startsWith("#")) { | |
| // 匹配 #varName 格式 | |
| return str.substring(1); | |
| } else if (str.startsWith("$")) { | |
| // 匹配 $varName 格式 | |
| return str.substring(1); | |
| } | |
| return null; | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment