import json import re def generate_key_paths(data, parent_key='', good_list=None, seen=None): """ 生成嵌套字典中的键路径,并提取最内层的键名。 同时,提取特定模式的键(如 '交换机-1', '交换机-2')的父路径。 如果同一层级下只有'交换机-1'但没有'交换机-2',则视为错误输入,删除后缀'-1',将'交换机'加入key_paths。 并从data中移除错误的键。 参数: data (dict): 输入的字典数据 parent_key (str): 上级键路径,用于递归调用 good_list (list): 用于存储去重后的最内层键名 seen (set): 用于跟踪已添加到 good_list 的元素 返回: tuple: 包含键路径列表、最内层键名列表、分组路径列表以及 no_keys_added 的元组 (key_paths, good_list, grouped_paths, no_keys_added) """ if good_list is None: good_list = [] if seen is None: seen = set() key_paths = [] grouped_paths = set() # 使用集合避免重复路径 no_keys_added = True # 默认假设没有添加任何键 # Step 1: Collect keys that match the pattern pattern = re.compile(r'(.+)-\d+$') # 匹配形如 '交换机-1', '交换机-2' 的键 prefix_groups = {} other_keys = [] for key in list(data.keys()): # 使用 list(data.keys()) 防止修改字典时出错 clean_key = key.replace(" ", "") match = pattern.match(clean_key) if match: prefix = match.group(1) if prefix not in prefix_groups: prefix_groups[prefix] = [] prefix_groups[prefix].append(key) else: other_keys.append(key) # Step 2: Handle grouped keys for prefix, keys in prefix_groups.items(): current_prefix_path = f"{parent_key}.{prefix}" if parent_key else prefix if len(keys) > 1: # 多个键匹配同一前缀:添加到 grouped_paths grouped_paths.add(current_prefix_path) if prefix not in seen: good_list.append(prefix) seen.add(prefix) no_keys_added = False else: # 只有一个键匹配:删除后缀并添加到 key_paths,同时从 data 中移除该键 key = keys[0] key_path = current_prefix_path # 去掉后缀后,路径为父路径 + 前缀 key_paths.append(key_path) if prefix not in seen: good_list.append(prefix) seen.add(prefix) no_keys_added = False # 从 data 中移除错误的键 data.pop(key) # Step 3: Handle other keys for key in other_keys: value = data[key] current_key = f"{parent_key}.{key}" if parent_key else key if isinstance(value, dict): if value: # 递归调用,并获取子路径、子 good_list、子分组路径以及子 no_keys_added sub_key_paths, _, sub_grouped_paths, sub_no_keys_added = generate_key_paths( value, current_key, good_list, seen ) key_paths.extend(sub_key_paths) grouped_paths.update(sub_grouped_paths) # 合并子分组路径到当前分组路径 # 更新 no_keys_added no_keys_added = no_keys_added and sub_no_keys_added else: # 空字典视为叶子节点 clean_key = key.replace(" ", "") key_paths.append(current_key.replace(" ", "")) if clean_key not in seen: good_list.append(clean_key) # 去掉空格后添加 seen.add(clean_key) # 更新 no_keys_added no_keys_added = False elif isinstance(value, list): # 列表类型视为叶子节点,无论是否为空 key_paths.append(current_key.replace(" ", "")) clean_key = key.replace(" ", "") if clean_key not in seen: good_list.append(clean_key) # 去掉空格后添加 seen.add(clean_key) # 更新 no_keys_added no_keys_added = False elif value in {"未知", "", "/"}: # 特定值视为叶子节点 key_paths.append(current_key.replace(" ", "")) clean_key = key.replace(" ", "") if clean_key not in seen: good_list.append(clean_key) # 去掉空格后添加 seen.add(clean_key) # 更新 no_keys_added no_keys_added = False else: # 其他情况视为叶子节点 key_paths.append(current_key.replace(" ", "")) clean_key = key.replace(" ", "") if clean_key not in seen: good_list.append(clean_key) # 去掉空格后添加 seen.add(clean_key) # 更新 no_keys_added no_keys_added = False return key_paths, good_list, grouped_paths, no_keys_added # 示例使用 data1 = { "采购需求": { "高清数字枪机-1": [], "枪机支架-1": [], "高清数字半球机-1": [], "网络硬盘录像机-1": [], "监硬控硬盘-1": [], "交换机-1": [], "交换机-2": [], "监视器-1": [], "电源线-1": [], "网线-1": [], "水晶头-1": [], "PVC线槽-1": [], "辅料-1": [], "安装调试-1": [], "视频图像采集及存储系统": { "系统功能": [] } } } key_paths, good_list, grouped_paths, no_keys_added = generate_key_paths(data1) print(json.dumps(data1,ensure_ascii=False,indent=4))