如何用XSLT拆分嵌套XML生成带ID的双表?附实现疑问
解决方案:处理NaaccrData XML生成结构化表格并分配唯一ID
一、XSLT是否是最优方案?
对于100+MB的XML文件,XSLT配合lxml是可行的——lxml的XSLT引擎效率足够处理这个量级的文件,且XML结构转换逻辑清晰、易维护。如果现有代码已基于lxml,继续用XSLT是低成本的选择。若后续有复杂业务逻辑,Python原生流式解析(如iterparse)也可替代,但XSLT在结构化转换场景下更简洁。
二、XSLT实现:生成患者+肿瘤表格(带唯一ID)
假设你的NaaccrData简化结构如下:
<NaaccrData> <Patient> <PatientId>原患者ID</PatientId> <Name>张三</Name> <Tumor> <Site>肺癌</Site> <Stage>I</Stage> </Tumor> <Tumor> <Site>胃癌</Site> <Stage>II</Stage> </Tumor> </Patient> <!-- 更多Patient节点 --> </NaaccrData>
1. 生成患者表格(带唯一序列ID)
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="text" encoding="UTF-8"/> <xsl:template match="/NaaccrData"> <!-- 输出CSV表头 --> <xsl:text>PatientUniqueId,OriginalPatientId,Name </xsl:text> <xsl:apply-templates select="Patient"/> </xsl:template> <xsl:template match="Patient"> <!-- 用节点全局位置作为患者唯一ID --> <xsl:value-of select="position()"/> <xsl:text>,</xsl:text> <xsl:value-of select="PatientId"/> <xsl:text>,</xsl:text> <xsl:value-of select="Name"/> <xsl:text> </xsl:text> </xsl:template> </xsl:stylesheet>
2. 生成肿瘤表格(带关联患者ID和自身唯一ID)
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="text" encoding="UTF-8"/> <xsl:template match="/NaaccrData"> <xsl:text>TumorUniqueId,PatientUniqueId,Site,Stage </xsl:text> <xsl:apply-templates select="Patient/Tumor"/> </xsl:template> <xsl:template match="Tumor"> <!-- 肿瘤全局唯一ID --> <xsl:value-of select="position()"/> <xsl:text>,</xsl:text> <!-- 关联的患者唯一ID(父节点Patient的全局位置) --> <xsl:value-of select="count(preceding::Patient) + 1"/> <xsl:text>,</xsl:text> <xsl:value-of select="Site"/> <xsl:text>,</xsl:text> <xsl:value-of select="Stage"/> <xsl:text> </xsl:text> </xsl:template> </xsl:stylesheet>
三、Python lxml处理流程
方式1:XSLT转换生成两个CSV
from lxml import etree # 加载XML文件(lxml支持100MB级文件的内存加载) xml_tree = etree.parse("naaccr_data.xml") # 生成患者CSV patient_xslt = etree.XSLT(etree.parse("patient_transform.xsl")) with open("patients.csv", "w", encoding="utf-8") as f: f.write(str(patient_xslt(xml_tree))) # 生成肿瘤CSV tumor_xslt = etree.XSLT(etree.parse("tumor_transform.xsl")) with open("tumors.csv", "w", encoding="utf-8") as f: f.write(str(tumor_xslt(xml_tree)))
方式2:流式解析(超大型文件更省内存)
如果文件超过500MB,用iterparse逐节点处理,避免一次性加载整个XML:
import csv # 初始化CSV写入器 with open("patients.csv", "w", encoding="utf-8", newline="") as p_file, \ open("tumors.csv", "w", encoding="utf-8", newline="") as t_file: p_writer = csv.writer(p_file) t_writer = csv.writer(t_file) # 写入表头 p_writer.writerow(["PatientUniqueId", "OriginalPatientId", "Name"]) t_writer.writerow(["TumorUniqueId", "PatientUniqueId", "Site", "Stage"]) patient_id = 1 tumor_global_id = 1 # 流式遍历Patient节点 for event, elem in etree.iterparse("naaccr_data.xml", tag="Patient"): # 写入患者数据 p_writer.writerow([ patient_id, elem.findtext("PatientId", ""), elem.findtext("Name", "") ]) # 写入当前患者的肿瘤数据 for tumor in elem.findall("Tumor"): t_writer.writerow([ tumor_global_id, patient_id, tumor.findtext("Site", ""), tumor.findtext("Stage", "") ]) tumor_global_id += 1 elem.clear() # 释放节点内存 patient_id += 1
四、关键注意事项
- 序列ID规则:若需跨会话保持唯一,可替换为UUID,但按需求优先用自增数字即可。
- CSV转义:务必用
csv库处理字段中的逗号、换行符,避免手动拼接文本导致格式错误。 - 内存优化:流式解析适合超大型文件,XSLT方式更适合逻辑清晰的结构化转换场景。
内容的提问来源于stack exchange,提问作者wibeasley
相关产品推荐
相关产品推荐

