Python基于etree将CSV按双字段分组生成嵌套XML的实现问题
问题修复方案
完整修改后代码
from lxml import etree from itertools import groupby import csv # 注意文件名要加引号,否则会报语法错误 with open('Leverans_python.csv', encoding='utf-8') as data_file: reader = csv.DictReader(data_file) # 先按Personnummer排序,再按Anstallningsnummer排序,保证两级groupby正常工作 data = sorted(list(reader), key=lambda x: (x['Personnummer'], x['Anstallningsnummer'])) root = etree.Element('Arbetstagarlista') # 第一级分组:按Personnummer for person_id, person_group in groupby(data, key=lambda x: x['Personnummer']): # 创建Arbetstagare节点,添加Personnummer属性,解决带属性节点生成的问题 arbetstagare = etree.SubElement(root, 'Arbetstagare', attrib={'Personnummer': person_id}) # 把当前员工的所有记录转成列表,用于第二级分组 person_records = list(person_group) # 第二级分组:按Anstallningsnummer,解决多层嵌套分组的问题 for anstallning_id, anstallning_group in groupby(person_records, key=lambda x: x['Anstallningsnummer']): # 创建Anstallning节点,添加Anstallningsnummer属性 anstallning = etree.SubElement(arbetstagare, 'Anstallning', attrib={'Anstallningsnummer': anstallning_id}) # 遍历当前雇佣编号下的所有记录,每个记录对应一个Period节点 for entry in anstallning_group: period = etree.SubElement(anstallning, 'Period') # 批量添加子节点,避免重复代码 for field in ['From', 'Tom', 'Avtalstillhorighet', 'Pensionsalder', 'Anstallningstyp', 'Avslutsorsak']: field_node = etree.SubElement(period, field) field_node.text = entry[field] # 输出格式化XML with open('my_data.xml', 'wb') as out_file: out_file.write(etree.tostring(root, pretty_print=True, encoding='utf-8'))
修复逻辑说明
两级分组逻辑修复
你原来的代码只做了第一级按Personnummer的分组,并且在循环外层就提前创建了Anstallningsnummer和Period节点,导致同一个员工的所有雇佣记录都塞到了同一个节点下。修改后新增了第二级按Anstallningsnummer的分组,每次分组后才创建对应的Anstallning节点和Period节点,保证每个雇佣编号对应独立的节点结构。
注意groupby要求分组字段是连续有序的,所以排序的时候要同时按Personnummer和Anstallningsnummer两个字段排序,否则会出现同一个值被拆成多个分组的问题。带属性节点生成方法
使用etree.SubElement创建节点时,传入attrib参数即可给节点添加属性,你需要的<Arbetstagare Personnummer="xxx">结构,只需要把节点名设为Arbetstagare,再给attrib字典传入Personnummer对应的取值即可,不需要把节点名设为Personnummer。
内容的提问来源于stack exchange,提问作者Martin
相关产品推荐
相关产品推荐

