如何在Python LXML迭代写入时强制XML元素嵌套缩进?
问题描述
我用LXML生成数据库导出的XML文件,因为数据量太大,直接导出etree会把32GB内存的服务器撑爆,所以改用了LXML的增量XML生成方式,现在服务器只用5GB内存就能生成XML,但迭代写入的时候没法保留etree导出时的缩进格式。
试过etree.indent()方法,只能给Client节点内部加4空格缩进;硬编码空格又会导致标签错位。想知道怎么让Client的嵌套节点比当前层级多缩进4个空格。
附错误缩进的代码:
from lxml import etree client = etree.Element("Client") client.append(etree.Element("ClientID")) client[0].text = "12345" client.append(etree.Element("ClientProfile")) client[1].append(etree.Element("User")) client[1][0].append(etree.Element("FirstName")) client[1][0][0].text = "John" client[1][0].append(etree.Element("LastName")) client[1][0][1].text = "Doe" client[1][0].append(etree.Element("AccountName")) client[1][0][2].text = "Acme Inc" client[1][0].append(etree.Element("EmailAddress")) client[1][0][3].text = "user14@acmeinc.com" client.append(etree.Element("SalesMetric")) client[2].text = "12345" filename = "test.xml" with etree.xmlfile(filename, encoding="utf-8") as xml_file: with xml_file.element("DbDump"): xml_file.write("\n ") version = etree.Element("ModelVersion") version.text = "1.2.1" xml_file.write(version, pretty_print=True) etree.indent(client, space=" ") for value in "12": xml_file.write(client, pretty_print=True)
解决方案
方法1:利用xmlfile.element上下文嵌套控制层级
增量写入时,xmlfile.element支持上下文管理器嵌套,能自动处理缩进层级。把Client节点的写入逻辑整合到父节点的上下文里,结合pretty_print即可实现正确缩进:
from lxml import etree def create_client(): # 提取Client节点创建逻辑,方便批量生成 client = etree.Element("Client") client.append(etree.Element("ClientID")) client[0].text = "12345" client.append(etree.Element("ClientProfile")) client[1].append(etree.Element("User")) client[1][0].append(etree.Element("FirstName")) client[1][0][0].text = "John" client[1][0].append(etree.Element("LastName")) client[1][0][1].text = "Doe" client[1][0].append(etree.Element("AccountName")) client[1][0][2].text = "Acme Inc" client[1][0].append(etree.Element("EmailAddress")) client[1][0][3].text = "user14@acmeinc.com" client.append(etree.Element("SalesMetric")) client[2].text = "12345" return client filename = "test.xml" with etree.xmlfile(filename, encoding="utf-8") as xml_file: # 根节点开启pretty_print,自动处理子节点缩进逻辑 with xml_file.element("DbDump", pretty_print=True): version = etree.Element("ModelVersion") version.text = "1.2.1" xml_file.write(version) # 批量写入Client节点,手动添加前置换行和父层级缩进 for _ in "12": client = create_client() xml_file.write("\n ") xml_file.write(client) # 补充根节点闭合前的换行,保证格式完整 xml_file.write("\n")
方法2:预生成带缩进的节点字符串再写入
如果不想调整上下文结构,可以先用etree.tostring生成带内部缩进的Client节点内容,再手动添加父层级的缩进后写入:
from lxml import etree def create_client(): client = etree.Element("Client") client.append(etree.Element("ClientID")) client[0].text = "12345" client.append(etree.Element("ClientProfile")) client[1].append(etree.Element("User")) client[1][0].append(etree.Element("FirstName")) client[1][0][0].text = "John" client[1][0].append(etree.Element("LastName")) client[1][0][1].text = "Doe" client[1][0].append(etree.Element("AccountName")) client[1][0][2].text = "Acme Inc" client[1][0].append(etree.Element("EmailAddress")) client[1][0][3].text = "user14@acmeinc.com" client.append(etree.Element("SalesMetric")) client[2].text = "12345" return client filename = "test.xml" with etree.xmlfile(filename, encoding="utf-8") as xml_file: with xml_file.element("DbDump"): xml_file.write("\n ") version = etree.Element("ModelVersion") version.text = "1.2.1" xml_file.write(version, pretty_print=True) for _ in "12": client = create_client() # 生成带内部缩进的Client字符串,去掉XML声明 client_str = etree.tostring(client, encoding='utf-8', pretty_print=True, xml_declaration=False) # 给每一行添加父层级的4空格缩进 indented_client = b"\n " + client_str.replace(b"\n", b"\n ") xml_file.write(indented_client) xml_file.write("\n")
关键说明
xmlfile.element的pretty_print=True仅处理当前层级下子节点的内部缩进,直接用xml_file.write()写入节点时,需要手动添加节点所在层级的前置空格和换行。etree.indent()只负责节点内部子节点的缩进对齐,不会给节点本身添加外部层级的缩进,所以必须配合手动写入的前置空格来匹配父节点的层级。
内容的提问来源于stack exchange,提问作者user3507825
相关产品推荐
相关产品推荐

