Python实现CSV转XML时嵌套节点结构生成问题求助
问题描述
我正在编写Python脚本,实现从CSV文件读取数据并输出为指定格式XML文件的功能,参考了Stack Overflow上的《With PYTHON convert CSV file to XML file》教程完成了初始脚本,脚本可正常输出数据,但无法生成目标XML要求的嵌套结构,所有字段均平级展示。
期望的XML结构
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <ImportAcademicExtract xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <AcademicExtract> <StudentId>StudentID</StudentId> <LastName>LastName</LastName> <FirstName>FirstName</FirstName> <MiddleName>MiddleName</MiddleName> <SocialSecurityNumber>SocialSecurityNumber</SocialSecurityNumber> <BirthDate>BirthDate</BirthDate> <GradeLevel>GradeLevel</GradeLevel> <SpecialProgramIndicator>SpecialProgramIndicator</SpecialProgramIndicator> <CIPCode>CIPCode</CIPCode> <RegisteredHours>RegisteredHours</RegisteredHours> <PostalAddresses> <PostalAddress> <AddressLine1>Address</AddressLine1> <AddressLine2>Address2ndLn</AddressLine2> <City>City</City> <State>State</State> <PostalCode>PostalCode</PostalCode> <CountryCode>Country</CountryCode> </PostalAddress> </PostalAddresses> <EmailAddresses> <EmailAddress>Email</EmailAddress> </EmailAddresses> <PhoneNumbers> <PhoneNumber>PhoneNumber</PhoneNumber> </PhoneNumbers> <AdmissionsTerm> <AdmissionTerm> <TermName>TermName</TermName> <AcademicYear>AcademicYear</AcademicYear> <AdmittedDate>AdmittedDate</AdmittedDate> </AdmissionTerm> </AdmissionsTerm> <EnrollmentTerms> <EnrollmentTerm> <TermName>TermNames</TermName> <AcademicYear>AcademicYears</AcademicYear> <CumulativeAttemptedHours>CumulativeAttemptedHours</CumulativeAttemptedHours> <CumulativeRegisteredHours>CumulativeRegisteredHours</CumulativeRegisteredHours> <CumulativeEarnedHours>CumulativeEarnedHours</CumulativeEarnedHours> <CumulativeGPA>CumulativeGPA</CumulativeGPA> <TermStartDate>TermStartDate</TermStartDate> <TermEndDate>TermEndDate</TermEndDate> <EnrollmentType>EnrollmentType</EnrollmentType> <EnrollmentStatus>EnrollmentStatus</EnrollmentStatus> <FirstTimeDegreeSeeking>FirstTimeDegreeSeeking</FirstTimeDegreeSeeking> <IntentToReturn>IntentToReturn</IntentToReturn> <WithdrawnDate>WithdrawnDate</WithdrawnDate> <ExtTermName>ExtTermName</ExtTermName> <AcademicYearStartDate>AcademicYearStartDate</AcademicYearStartDate> <AcademicYearEndDate>AcademicYearEndDate</AcademicYearEndDate> <ExtEnrollmentType>ExtEnrollmentType</ExtEnrollmentType> <DateOfDetermination>DateOfDetermination</DateOfDetermination> <LastDateOfAttendance>LastDateOfAttendance</LastDateOfAttendance> <SAPStatus>SAPStatus</SAPStatus> </EnrollmentTerm> </EnrollmentTerms> <AcademicPrograms> <AcademicProgram> <ProgramCredentialLevel>ProgramCredentialLevel</ProgramCredentialLevel> <ProgramName>ProgramName</ProgramName> <EdMajor1>EdMajor1</EdMajor1> <SiteName>SiteName</SiteName> <EffectiveStartDate>EffectiveStartDate</EffectiveStartDate> <EffectiveEndDate>EffectiveEndDate</EffectiveEndDate> <GraduationDate>GraduationDate</GraduationDate> <AnticipatedGraduationDate>AnticipatedGraduationDate</AnticipatedGraduationDate> <ProgramLengthInWeeks>ProgramLengthInWeeks</ProgramLengthInWeeks> <ProgramLengthInMonths>ProgramLengthInMonths</ProgramLengthInMonths> <ProgramLengthInYears>ProgramLengthInYears</ProgramLengthInYears> <AcademicYearBeginDate>AcademicYearBeginDate2</AcademicYearBeginDate> <AcademicYearEndDate>AcademicYearEndDate2</AcademicYearEndDate> <WeeksInProgramAcademicYear>WeeksInProgramAcademicYear</WeeksInProgramAcademicYear> </AcademicProgram> </AcademicPrograms> </AcademicExtract> </ImportAcademicExtract>
当前使用的代码
import itertools import csv import os csvFile = r'J:\JFAFiles\JFA-BR.csv' xmlFile = r'J:\JFAFiles\XML-BR.xml' csvData = csv.reader(open(csvFile)) xmlData = open(xmlFile, 'w') xmlData.write('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' + "\n" +'<ImportAcademicExtract xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">' + "\n" ) rowNum = 0 for row in csvData: if rowNum == 0: tags = row # replace spaces w/ underscores in tag names for i in range(len(tags)): tags[i] = tags[i].replace(' ', '_') else: xmlData.write(' '+'<AcademicExtract>' +"\n") for i in range (len(tags)): xmlData.write(' ' +'<' + tags[i] + '>' \ + row[i] + '</' + tags[i] + '>' + "\n") xmlData.write(' '+'</AcademicExtract>' + "\n") rowNum +=1 xmlData.write('</ImportAcademicExtract>' + "\n") xmlData.close()
当前错误输出样例(敏感信息已隐藏)
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <ImportAcademicExtract xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <AcademicExtract> <StudentID>____</StudentID> <LastName>____</LastName> <FirstName>____</FirstName> <MiddleName>____</MiddleName> <SocialSecurityNumber>____</SocialSecurityNumber> <BirthDate>____</BirthDate> <GradeLevel>____</GradeLevel> <SpecialProgramIndicator>____</SpecialProgramIndicator> <CIPCode>____</CIPCode> <RegisteredHours>____</RegisteredHours> <Address>____</Address> <Address2ndLn>____</Address2ndLn> <City>____</City> <State>____</State> <PostalCode>____</PostalCode> <Country>____</Country> <Email>____</Email> <PhoneNumber>____</PhoneNumber> <TermName>____</TermName> <AcademicYear>____</AcademicYear> <AdmittedDate>____</AdmittedDate> <TermName2>____</TermName2> <AcademicYear2>____</AcademicYear2> <CumulativeAttemptedHours>____</CumulativeAttemptedHours> <CumulativeRegisteredHours>____</CumulativeRegisteredHours> <CumulativeEarnedHours>____</CumulativeEarnedHours> <CumulativeGPA>____</CumulativeGPA> <TermStartDate>____</TermStartDate> <TermEndDate>____</TermEndDate> <EnrollmentType>____</EnrollmentType> <EnrollmentStatus>____</EnrollmentStatus> <FirstTimeDegreeSeeking>____</FirstTimeDegreeSeeking> <IntentToReturn>____</IntentToReturn> <WithdrawnDate>____</WithdrawnDate> <ExtTermName>____</ExtTermName> <AcademicYearStartDate>____</AcademicYearStartDate> <AcademicYearEndDate>____</AcademicYearEndDate> <ExtEnrollmentType>____</ExtEnrollmentType> <DateOfDetermination>____</DateOfDetermination> <LastDateOfAttendance>____</LastDateOfAttendance> <SAPStatus>____</SAPStatus> <ProgramCredentialLevel>____</ProgramCredentialLevel> <ProgramName>____</ProgramName> <EdMajor1>____</EdMajor1> <SiteName>____</SiteName> <EffectiveStartDate>____</EffectiveStartDate> <EffectiveEndDate>____</EffectiveEndDate> <GraduationDateDate>____</GraduationDateDate> <AnticipatedGraduationDate>____</AnticipatedGraduationDate> <ProgramLengthInWeeks>____</ProgramLengthInWeeks> <ProgramLengthInMonths>____</ProgramLengthInMonths> <ProgramLengthInYears>____</ProgramLengthInYears> <AcademicYearBeginDate2>____</AcademicYearBeginDate2> <AcademicYearEndDate2>____</AcademicYearEndDate2> <WeeksInProgramAcademicYear>____</WeeksInProgramAcademicYear> </AcademicExtract> </ImportAcademicExtract>
解决方案
核心思路是先预定义CSV列名到XML标签的映射关系,以及嵌套分组的层级结构,输出时按层级逐个写入节点即可,修改后的完整代码如下:
import csv csvFile = r'J:\JFAFiles\JFA-BR.csv' xmlFile = r'J:\JFAFiles\XML-BR.xml' # 定义CSV列名到XML标签的映射,没有特殊映射的默认和列名一致 tag_mapping = { 'StudentID': 'StudentId', 'Address': 'AddressLine1', 'Country': 'CountryCode', 'TermName2': 'TermName', 'AcademicYear2': 'AcademicYear', 'GraduationDateDate': 'GraduationDate', 'AcademicYearBeginDate2': 'AcademicYearBeginDate', 'AcademicYearEndDate2': 'AcademicYearEndDate' } # 定义嵌套分组结构:外层节点名 → 内层节点名 → 该分组包含的CSV列名 group_config = [ { 'outer_tag': 'PostalAddresses', 'inner_tag': 'PostalAddress', 'fields': ['Address', 'Address2ndLn', 'City', 'State', 'PostalCode', 'Country'] }, { 'outer_tag': 'EmailAddresses', 'inner_tag': 'EmailAddress', 'fields': ['Email'] }, { 'outer_tag': 'PhoneNumbers', 'inner_tag': 'PhoneNumber', 'fields': ['PhoneNumber'] }, { 'outer_tag': 'AdmissionsTerm', 'inner_tag': 'AdmissionTerm', 'fields': ['TermName', 'AcademicYear', 'AdmittedDate'] }, { 'outer_tag': 'EnrollmentTerms', 'inner_tag': 'EnrollmentTerm', 'fields': ['TermName2', 'AcademicYear2', 'CumulativeAttemptedHours', 'CumulativeRegisteredHours', 'CumulativeEarnedHours', 'CumulativeGPA', 'TermStartDate', 'TermEndDate', 'EnrollmentType', 'EnrollmentStatus', 'FirstTimeDegreeSeeking', 'IntentToReturn', 'WithdrawnDate', 'ExtTermName', 'AcademicYearStartDate', 'AcademicYearEndDate', 'ExtEnrollmentType', 'DateOfDetermination', 'LastDateOfAttendance', 'SAPStatus'] }, { 'outer_tag': 'AcademicPrograms', 'inner_tag': 'AcademicProgram', 'fields': ['ProgramCredentialLevel', 'ProgramName', 'EdMajor1', 'SiteName', 'EffectiveStartDate', 'EffectiveEndDate', 'GraduationDateDate', 'AnticipatedGraduationDate', 'ProgramLengthInWeeks', 'ProgramLengthInMonths', 'ProgramLengthInYears', 'AcademicYearBeginDate2', 'AcademicYearEndDate2', 'WeeksInProgramAcademicYear'] } ] # 定义一级字段(AcademicExtract下直接展示的字段) root_fields = ['StudentID', 'LastName', 'FirstName', 'MiddleName', 'SocialSecurityNumber', 'BirthDate', 'GradeLevel', 'SpecialProgramIndicator', 'CIPCode', 'RegisteredHours'] with open(csvFile, 'r', encoding='utf-8') as f_in, open(xmlFile, 'w', encoding='utf-8') as f_out: reader = csv.DictReader(f_in) # 处理CSV列名,替换空格为下划线 reader.fieldnames = [fn.replace(' ', '_') for fn in reader.fieldnames] # 写入XML头和根节点 f_out.write('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n') f_out.write('<ImportAcademicExtract xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">\n') for row in reader: f_out.write(' <AcademicExtract>\n') # 先写一级字段 for field in root_fields: tag = tag_mapping.get(field, field) f_out.write(f' <{tag}>{row[field]}</{tag}>\n') # 写各个嵌套分组 for group in group_config: outer_tag = group['outer_tag'] inner_tag = group['inner_tag'] fields = group['fields'] # 写外层节点 f_out.write(f' <{outer_tag}>\n') f_out.write(f' <{inner_tag}>\n') # 写分组内的字段 for field in fields: tag = tag_mapping.get(field, field) f_out.write(f' <{tag}>{row[field]}</{tag}>\n') # 闭合内层和外层节点 f_out.write(f' </{inner_tag}>\n') f_out.write(f' </{outer_tag}>\n') f_out.write(' </AcademicExtract>\n') # 闭合根节点 f_out.write('</ImportAcademicExtract>\n')
如果你的CSV列名和上面定义的有出入,调整tag_mapping和group_config里的字段名即可。
内容的提问来源于stack exchange,提问作者daf0555
相关产品推荐
相关产品推荐

