Python字典转XML脚本生成重复FirstChild元素,求修正方案
问题:字典转XML工具重复生成FirstChild元素的修正
我正在开发一款通过config_table定义节点关系的通用字典转XML工具,当前Python脚本输出接近预期,但每个数据项都会生成独立的FirstChild元素,导致该元素重复。以下是脚本、实际输出和预期输出,求修正:
import xml.etree.ElementTree as ET # Define the config_table and input_data config_table = [ {'NODE_ID': 0, 'NODE_NAME': 'RootElement', 'NODE_TYPE': 'element', 'PARENT_ID': None, 'VALUE_FIELD': None, 'KEY_FIELD': 'KEY_ZERO'}, {'NODE_ID': 1, 'NODE_NAME': 'FirstChild', 'NODE_TYPE': 'element', 'PARENT_ID': 0, 'VALUE_FIELD': None, 'KEY_FIELD': 'KEY_ONE'}, {'NODE_ID': 2, 'NODE_NAME': 'SecondChild', 'NODE_TYPE': 'element', 'PARENT_ID': 1, 'VALUE_FIELD': None, 'KEY_FIELD': 'KEY_TWO'}, {'NODE_ID': 3, 'NODE_NAME': 'SecAtt', 'NODE_TYPE': 'attribute', 'PARENT_ID': 2, 'VALUE_FIELD': 'VALUE_THREE', 'KEY_FIELD': 'VALUE_THREE'}, {'NODE_ID': 4, 'NODE_NAME': 'ThirdChild', 'NODE_TYPE': 'text', 'PARENT_ID': 2, 'VALUE_FIELD': '', 'KEY_FIELD': 'VALUE_FOUR'}, ] input_data = [ {'KEY_ZERO': 1, 'KEY_ONE': 1, 'KEY_TWO': 1, 'VALUE_THREE': 'alpha', 'VALUE_FOUR': 'The first letter of the Greek alphabet'}, {'KEY_ZERO': 1, 'KEY_ONE': 1, 'KEY_TWO': 2, 'VALUE_THREE': 'beta', 'VALUE_FOUR': 'The second letter of the Greek alphabet'}, {'KEY_ZERO': 1, 'KEY_ONE': 1, 'KEY_TWO': 3, 'VALUE_THREE': 'gamma', 'VALUE_FOUR': 'The third letter of the Greek alphabet'}, {'KEY_ZERO': 1, 'KEY_ONE': 2, 'KEY_TWO': 4, 'VALUE_THREE': 'ay', 'VALUE_FOUR': 'The first letter of the English alphabet'}, {'KEY_ZERO': 1, 'KEY_ONE': 3, 'KEY_TWO': 5, 'VALUE_THREE': 'alif', 'VALUE_FOUR': 'The first letter of the Arabic alphabet'} ] # Function to create XML from the config_table and input_data def create_xml(config_table, input_data): # Helper function to find node by ID def find_node_by_id(node_id): for node in config_table: if node['NODE_ID'] == node_id: return node return None # Helper function to build XML recursively def build_xml(parent, parent_node_id, data): for node in config_table: if node['PARENT_ID'] == parent_node_id: if node['NODE_TYPE'] == 'element': element = ET.SubElement(parent, node['NODE_NAME']) # Add KEY_FIELD attributes #if node['KEY_FIELD'] in data: #element.set(node['KEY_FIELD'], str(data[node['KEY_FIELD']])) build_xml(element, node['NODE_ID'], data) elif node['NODE_TYPE'] == 'attribute': parent.set(node['NODE_NAME'], data[node['VALUE_FIELD']]) elif node['NODE_TYPE'] == 'text': element = ET.SubElement(parent,node['NODE_NAME']) element.text = data[node['KEY_FIELD']] # Add KEY_FIELD attributes at each level #if node['KEY_FIELD'] in data: #parent.set(node['KEY_FIELD'], str(data[node['KEY_FIELD']])) # Create the root element root_node = find_node_by_id(0) root = ET.Element(root_node['NODE_NAME']) # Build the XML tree for each item in the input data for data in input_data: build_xml(root, 0, data) # Convert the tree to a string xml_str = ET.tostring(root, encoding='unicode', method='xml') return xml_str # Generate the XML xml_output = create_xml(config_table, input_data) print(xml_output)
实际输出
<RootElement> <FirstChild> <SecondChild SecAtt="alpha"> <ThirdChild>The first letter of the Greek alphabet</ThirdChild> </SecondChild> </FirstChild> <FirstChild> <SecondChild SecAtt="beta"> <ThirdChild>The second letter of the Greek alphabet</ThirdChild> </SecondChild> </FirstChild> <FirstChild> <SecondChild SecAtt="gamma"> <ThirdChild>The third letter of the Greek alphabet</ThirdChild> </SecondChild> </FirstChild> <FirstChild> <SecondChild SecAtt="ay"> <ThirdChild>The first letter of the English alphabet</ThirdChild> </SecondChild> </FirstChild> <FirstChild> <SecondChild SecAtt="alif"> <ThirdChild>The first letter of the Arabic alphabet</ThirdChild> </SecondChild> </FirstChild> </RootElement>
预期输出
<RootElement> <FirstChild> <SecondChild SecAtt="alpha"> <ThirdChild>The first letter of the Greek alphabet</ThirdChild> </SecondChild> <SecondChild SecAtt="beta"> <ThirdChild>The second letter of the Greek alphabet</ThirdChild> </SecondChild> <SecondChild SecAtt="gamma"> <ThirdChild>The third letter of the Greek alphabet</ThirdChild> </SecondChild> </FirstChild> <FirstChild> <SecondChild SecAtt="ay"> <ThirdChild>The first letter of the English alphabet</ThirdChild> </SecondChild> </FirstChild> <FirstChild> <SecondChild SecAtt="alif"> <ThirdChild>The first letter of the Arabic alphabet</ThirdChild> </SecondChild> </FirstChild> </RootElement>
解决方案
核心问题是当前脚本为每条数据从头构建完整节点树,导致相同KEY_ONE值的FirstChild被重复创建。需要先按FirstChild对应的KEY_FIELD(即KEY_ONE)对输入数据分组,再针对每组数据构建节点:同一组的所有数据共享一个FirstChild节点,将对应的SecondChild挂到该节点下。
修改后的create_xml函数如下:
def create_xml(config_table, input_data): # Helper function to find node by ID def find_node_by_id(node_id): for node in config_table: if node['NODE_ID'] == node_id: return node return None # Helper function to build XML starting from a specific node ID def build_xml_from_node(parent, start_node_id, data): for node in config_table: if node['NODE_ID'] == start_node_id: if node['NODE_TYPE'] == 'element': element = ET.SubElement(parent, node['NODE_NAME']) # Recursively build child nodes build_child_nodes(element, node['NODE_ID'], data) elif node['NODE_TYPE'] == 'attribute': parent.set(node['NODE_NAME'], data[node['VALUE_FIELD']]) elif node['NODE_TYPE'] == 'text': element = ET.SubElement(parent, node['NODE_NAME']) element.text = data[node['KEY_FIELD']] # Helper function to build child nodes recursively def build_child_nodes(parent_element, parent_node_id, data): for node in config_table: if node['PARENT_ID'] == parent_node_id: if node['NODE_TYPE'] == 'element': element = ET.SubElement(parent_element, node['NODE_NAME']) build_child_nodes(element, node['NODE_ID'], data) elif node['NODE_TYPE'] == 'attribute': parent_element.set(node['NODE_NAME'], data[node['VALUE_FIELD']]) elif node['NODE_TYPE'] == 'text': element = ET.SubElement(parent_element, node['NODE_NAME']) element.text = data[node['KEY_FIELD']] # Create the root element root_node = find_node_by_id(0) root = ET.Element(root_node['NODE_NAME']) # Get FirstChild's KEY_FIELD to group data first_child_node = find_node_by_id(1) group_key = first_child_node['KEY_FIELD'] # Group input_data by the group_key (KEY_ONE) from collections import defaultdict grouped_data = defaultdict(list) for data in input_data: grouped_data[data[group_key]].append(data) # Build XML for each group for data_list in grouped_data.values(): # Create FirstChild element under root first_child = ET.SubElement(root, first_child_node['NODE_NAME']) # For each data in the group, build from SecondChild (NODE_ID=2) for data in data_list: build_xml_from_node(first_child, 2, data) # Convert the tree to a string xml_str = ET.tostring(root, encoding='unicode', method='xml') return xml_str
运行修改后的代码即可得到预期的XML输出。
内容的提问来源于stack exchange,提问作者user1386812
相关产品推荐
相关产品推荐

