如何从非标准链表式XML结构中提取完整树形结构?
解决思路
你的核心问题在于节点标识不统一(同时用元素对象和字符串ID),以及没有把节点的附加信息和树形关系绑定起来。下面是分步修复并实现完整树形提取的方案:
1. 先建立ID到节点元素的映射
首先需要把XML中每个<BB>节点的ID(对应<NODE>文本)和元素本身关联起来,后续才能通过ID快速找到节点的附加信息:
from lxml import etree import networkx as nx tree = etree.parse('file.xml') root = tree.getroot() # 处理命名空间,根据你的XML实际命名空间调整,若没有可省略ns映射 ns = {"ns": root.nsmap.get(None, "")} if root.nsmap else {} # 构建ID → <BB>元素的映射 id_to_bb = {} for bb_node in root.xpath('//ns:BB', namespaces=ns): # 提取当前节点的ID(从同节点下的<NODE>文本) node_id = bb_node.xpath('./ns:NODE/text()', namespaces=ns)[0].strip() id_to_bb[node_id] = bb_node
2. 正确构建树形图(父→子)
之前的代码把<HEAD_NODE>元素直接作为图节点,导致节点标识和边的ID不匹配。现在统一用节点ID作为图的节点,同时把附加信息存入节点属性:
G = nx.DiGraph() # 添加所有节点,并存储附加信息(过滤掉HEAD_NODE和NODE) for node_id, bb_node in id_to_bb.items(): attrs = {} # 提取当前BB节点下除HEAD_NODE、NODE外的所有附加信息 for elem in bb_node.xpath('./*[not(local-name()="HEAD_NODE" or local-name()="NODE")]', namespaces=ns): attrs[elem.localname] = elem.text.strip() if elem.text else "" G.add_node(node_id, **attrs) # 添加父→子的边:HEAD_NODE的文本是父节点ID,当前节点ID是子节点 for node_id, bb_node in id_to_bb.items(): parent_id = bb_node.xpath('./ns:HEAD_NODE/text()', namespaces=ns)[0].strip() # 跳过不存在的父节点(比如根节点的父ID可能为特殊值) if parent_id in id_to_bb: G.add_edge(parent_id, node_id)
3. 从叶节点向上遍历完整路径
基于已构建的图,从你拿到的叶节点出发,获取到根节点的完整路径,同时输出节点附加信息:
# 处理你已获取的叶节点HEAD_NODE元素 leaf_head_nodes = root.xpath('//*[local-name()="BB"][not(.//*[local-name()="BB"])]/*[local-name()="HEAD_NODE"]') # 先找到所有根节点(入度为0的节点,即无父节点的节点) root_nodes = [n for n, d in G.in_degree() if d == 0] for leaf_head in leaf_head_nodes: # 获取叶节点自身的ID leaf_bb = leaf_head.getparent() leaf_id = leaf_bb.xpath('./ns:NODE/text()', namespaces=ns)[0].strip() # 输出从根到叶的路径及节点信息 for root_id in root_nodes: if nx.has_path(G, root_id, leaf_id): path = nx.shortest_path(G, root_id, leaf_id) print(f"完整路径: {' → '.join(path)}") for node_id in path: print(f"\n节点ID: {node_id}") # 输出附加信息 for attr_key, attr_val in G.nodes[node_id].items(): print(f" {attr_key}: {attr_val}")
4. 可选:构建嵌套字典形式的树形结构
如果需要把树形结构转成更易处理的嵌套字典(方便序列化或后续业务逻辑),可以递归生成:
def build_nested_tree(node_id, graph, id_map): tree_data = {"id": node_id, **graph.nodes[node_id]} # 获取当前节点的所有子节点 children = [build_nested_tree(child_id, graph, id_map) for child_id in graph.successors(node_id)] if children: tree_data["children"] = children return tree_data # 生成根节点的嵌套树 for root_id in root_nodes: nested_tree = build_nested_tree(root_id, G, id_to_bb) # 可转成JSON输出 import json print(json.dumps(nested_tree, indent=2, ensure_ascii=False))
内容的提问来源于stack exchange,提问作者Michal
相关产品推荐
相关产品推荐

