Python修改XML中CDATA标签内容后保留CDATA格式的方法
问题描述
我在Python和XML使用方面经验不足,尝试用Python解析XML文件,修改CDATA标签内的名称并生成新XML文件。使用从Stack Overflow获取的代码可完成修改并生成新文件,但新文件丢失了<![CDATA[]]>格式。请问如何让Python写入新XML时保留该格式?
原XML文件:
<Programs Use="Context"> <Program Use="Target" Name="MainProgram" TestEdits="false" MainRoutineName="MainRoutine" Disabled="false" UseAsFolder="false"> <Tags/> <Routines> <Routine Name="MainRoutine" Type="RLL"> <RLLContent> <Rung Number="0" Type="N"> <Text> <![CDATA[XIC(AutoSlateAutoRun);]]> </Text> </Rung> </RLLContent> </Routine> </Routines> </Program> </Programs>
使用的Python代码:
def create_l5x_import_file(): with open('ProgramExport.L5X', encoding='UTF-8-SIG') as f: tree = ET.parse(f) root = tree.getroot() for elem in root.iter(): try: elem.text = elem.text.replace("(AutoSlateAutoRun)", "(THIS_IS_NEW)") except AttributeError: pass tree.write('ProgramOutput.L5X', xml_declaration=True, method='xml', encoding='UTF-8-SIG')
解决方案
Python标准库的xml.etree.ElementTree默认会把CDATA内容视为普通文本,写入XML时不会保留<![CDATA[]]>标记。要解决这个问题,需要手动将修改后的内容重新包裹为CDATA节点,以下是两种可行方法:
方法一:使用Python 3.8+自带的CDATA类
Python 3.8及以上版本的ElementTree提供了原生CDATA类,可直接创建CDATA节点,代码修改如下:
import xml.etree.ElementTree as ET def create_l5x_import_file(): with open('ProgramExport.L5X', encoding='UTF-8-SIG') as f: tree = ET.parse(f) root = tree.getroot() # 遍历所有Text节点,处理CDATA内容 for elem in root.iter('Text'): if elem.text is not None: # 替换目标文本 new_content = elem.text.replace("(AutoSlateAutoRun)", "(THIS_IS_NEW)") # 清空原有内容,添加CDATA节点 elem.clear() elem.append(ET.CDATA(new_content)) tree.write('ProgramOutput.L5X', xml_declaration=True, method='xml', encoding='UTF-8-SIG') create_l5x_import_file()
方法二:自定义序列化逻辑(兼容Python低版本)
如果你的Python版本低于3.8,可以通过重写ElementTree的序列化方法来支持CDATA:
import xml.etree.ElementTree as ET # 重写XML序列化方法,识别并输出CDATA节点 def _serialize_xml(write, elem, encoding, qnames, namespaces): if elem.tag is ET.CDATA: write(f"<![CDATA[{elem.text}]]>".encode(encoding)) return return ET._serialize_xml(write, elem, encoding, qnames, namespaces) ET._serialize_xml = _serialize_xml ET._serialize['xml'] = _serialize_xml def create_l5x_import_file(): with open('ProgramExport.L5X', encoding='UTF-8-SIG') as f: tree = ET.parse(f) root = tree.getroot() for elem in root.iter('Text'): if elem.text is not None: new_content = elem.text.replace("(AutoSlateAutoRun)", "(THIS_IS_NEW)") elem.clear() # 创建自定义CDATA节点 cdata_node = ET.Element(ET.CDATA) cdata_node.text = new_content elem.append(cdata_node) tree.write('ProgramOutput.L5X', xml_declaration=True, method='xml', encoding='UTF-8-SIG') create_l5x_import_file()
关键说明
- 原代码的核心问题:直接修改
elem.text会丢失CDATA标记,因为ElementTree将CDATA内容等同于普通文本。 - 两种方法的核心逻辑一致:先替换文本内容,再将内容包裹为CDATA节点替换原文本节点。
- 修正了原代码的缩进错误:原代码中
for循环和写入操作在函数外部,会导致root未定义的问题,需将其放入函数内部。
内容的提问来源于stack exchange,提问作者TumRums1989
相关产品推荐
相关产品推荐

