在数据处理的实际场景中,经常需要将扁平的CSV二维表数据转换为支持层级嵌套的XML格式,方便不同系统之间的数据交互。Python提供了内置的csv和xml.etree.ElementTree库,可以高效完成这个转换过程,不需要依赖第三方库就能实现需求。

转换前的准备
首先需要明确CSV数据的结构和目标XML的嵌套规则。假设我们有一个存储学生选课信息的CSV文件,内容如下:
学号,姓名,课程名称,课程分数 1001,张三,数学,90 1001,张三,语文,85 1002,李四,数学,88 1002,李四,英语,92
我们希望转换后的XML按照学生作为外层节点,每个学生的选课记录作为内层嵌套节点,结构如下:
<students>
<student>
<学号>1001</学号>
<姓名>张三</姓名>
<courses>
<course>
<课程名称>数学</课程名称>
<课程分数>90</课程分数>
</course>
<course>
<课程名称>语文</课程名称>
<课程分数>85</课程分数>
</course>
</courses>
</student>
<student>
<学号>1002</学号>
<姓名>李四</姓名>
<courses>
<course>
<课程名称>数学</课程名称>
<课程分数>88</课程分数>
</course>
<course>
<课程名称>英语</课程名称>
<课程分数>92</课程分数>
</course>
</courses>
</student>
</students>
核心实现步骤
1. 读取CSV数据
使用Python内置的csv模块读取CSV文件,将数据转换为字典列表,方便后续处理。注意需要指定文件的编码格式,避免中文乱码问题。
import csv
def read_csv(file_path):
# 读取CSV文件内容,返回字典列表
data = []
with open(file_path, 'r', encoding='utf-8') as f:
# 使用DictReader自动将第一行作为键
reader = csv.DictReader(f)
for row in reader:
data.append(row)
return data
# 读取示例CSV文件
csv_data = read_csv('student_course.csv')
2. 整理嵌套数据结构
CSV数据是扁平的,需要先将相同学生的记录聚合到一起,构建出符合XML嵌套要求的数据结构。这里使用字典来按学号分组,每个学号对应一个学生信息和他的课程列表。
def organize_data(raw_data):
# 按学号分组整理数据
student_dict = {}
for item in raw_data:
student_id = item['学号']
if student_id not in student_dict:
# 初始化学生信息,包含基本属性和课程列表
student_dict[student_id] = {
'学号': item['学号'],
'姓名': item['姓名'],
'courses': []
}
# 添加当前行的课程信息到对应学生的课程列表
course_info = {
'课程名称': item['课程名称'],
'课程分数': item['课程分数']
}
student_dict[student_id]['courses'].append(course_info)
# 返回学生信息列表
return list(student_dict.values())
# 整理后的嵌套数据
organized_data = organize_data(csv_data)
3. 构建XML节点并生成文件
使用xml.etree.ElementTree库构建XML的层级节点,先创建根节点,再遍历整理好的数据依次添加子节点,最后将XML树写入文件。注意XML节点名称如果包含中文,不需要额外处理,直接作为标签名即可。
import xml.etree.ElementTree as ET
def generate_xml(data, output_path):
# 创建根节点
root = ET.Element('students')
for student_info in data:
# 创建学生节点
student_node = ET.SubElement(root, 'student')
# 添加学生基本属性节点
for key in ['学号', '姓名']:
child = ET.SubElement(student_node, key)
child.text = student_info[key]
# 创建课程列表节点
courses_node = ET.SubElement(student_node, 'courses')
# 遍历课程列表,添加每个课程节点
for course in student_info['courses']:
course_node = ET.SubElement(courses_node, 'course')
for c_key, c_value in course.items():
c_child = ET.SubElement(course_node, c_key)
c_child.text = c_value
# 创建XML树
tree = ET.ElementTree(root)
# 写入文件,指定编码为utf-8,xml_declaration添加XML声明
tree.write(output_path, encoding='utf-8', xml_declaration=True)
# 生成XML文件
generate_xml(organized_data, 'output.xml')
注意事项
- CSV文件读取时需要确认编码格式,如果文件是GBK编码,需要将open函数的encoding参数改为gbk,否则会出现乱码。
- 如果CSV的列名包含特殊字符,需要提前处理成符合XML标签命名规则的字符串,XML标签名不能以数字开头,也不能包含空格和大部分特殊符号。
- 生成的XML文件如果需要格式化缩进,可以使用minidom模块对输出内容进行美化,默认生成的XML是紧凑格式的。
美化XML输出(可选)
如果需要生成带缩进格式的美观XML,可以在写入文件前使用xml.dom.minidom处理内容:
import xml.dom.minidom as minidom
def pretty_generate_xml(data, output_path):
root = ET.Element('students')
for student_info in data:
student_node = ET.SubElement(root, 'student')
for key in ['学号', '姓名']:
child = ET.SubElement(student_node, key)
child.text = student_info[key]
courses_node = ET.SubElement(student_node, 'courses')
for course in student_info['courses']:
course_node = ET.SubElement(courses_node, 'course')
for c_key, c_value in course.items():
c_child = ET.SubElement(course_node, c_key)
c_child.text = c_value
# 将ElementTree转换为字符串
rough_string = ET.tostring(root, encoding='utf-8')
# 使用minidom解析并美化
dom = minidom.parseString(rough_string)
pretty_xml = dom.toprettyxml(indent=' ', encoding='utf-8')
# 写入文件,去掉minidom自动添加的空行
with open(output_path, 'wb') as f:
f.write(pretty_xml)
# 生成美化后的XML文件
pretty_generate_xml(organized_data, 'pretty_output.xml')