如何用Python的lxml在XML的testcases节点下追加新节点?
问题描述
尝试用lxml的append函数在XML的testcases节点内追加新节点时,出现错误:
AttributeError: 'NoneType' object has no attribute 'append'
将节点追加到question父节点时无报错,但无法得到预期输出。
复现代码
from lxml import etree tree = etree.parse("d_xml.xml") d='<testcase testtype="0" useasexample="0" hiderestiffail="0" mark="1.0000000" > <testcode> <text>print(solve(' \ 'read_file("file_2.txt")))</text> </testcode><stdin><text></text></stdin><expected><text>status_2</text></expected' \ '><extra><text></text></extra><display><text>SHOW</text> </display></testcase>' # contentnav = tree.find("question") contentnav = tree.find("testcases") contentnav.append(etree.XML(d)) print(etree.tostring(tree)) tree.write('output.xml', pretty_print=True, xml_declaration=True, encoding="utf-8")
原XML文件(d_xml.xml)
<?xml version="1.0" encoding="UTF-8"?> <quiz> <!-- question: 0 --> <!-- question: 6675757 --> <question type="coderunner"> <name> <text>test_code_runner</text> </name> <questiontext format="html"> <text><![CDATA[<p dir="ltr" style="text-align: left;">ex_test<br></p>]]></text> </questiontext> <generalfeedback format="html"> <text></text> </generalfeedback> <giveupallowed>0</giveupallowed> <prototypeextra></prototypeextra> <testcases> <testcase testtype="0" useasexample="0" hiderestiffail="0" mark="1.0000000"> <testcode> <text>print(solve(read_file('file_1.txt')))</text> </testcode> <stdin> <text></text> </stdin> <expected> <text>status_1</text> </expected> <extra> <text></text> </extra> <display> <text>SHOW</text> </display> </testcase> <file name="fmlie.cpp" path="/" encoding="base64">aW1wb3J0IHJhbmRvbH</file> <file name="idfile.txt" path="/" encoding="base64">TnVA2DQoyICg==</file> </testcases> </question> </quiz>
预期输出
<?xml version="1.0" encoding="UTF-8"?> <quiz> <!-- question: 0 --> <!-- question: 6675757 --> <question type="coderunner"> <name> <text>test_code_runner</text> </name> <questiontext format="html"> <text><![CDATA[<p dir="ltr" style="text-align: left;">ex_test<br></p>]]></text> </questiontext> <generalfeedback format="html"> <text></text> </generalfeedback> <giveupallowed>0</giveupallowed> <prototypeextra></prototypeextra> <testcases> <testcase testtype="0" useasexample="0" hiderestiffail="0" mark="1.0000000"> <testcode> <text>print(solve(read_file('file_1.txt')))</text> </testcode> <stdin> <text></text> </stdin> <expected> <text>status_1</text> </expected> <extra> <text></text> </extra> <display> <text>SHOW</text> </display> </testcase> <testcase testtype="0" useasexample="0" hiderestiffail="0" mark="1.0000000"> <testcode> <text>print(solve(read_file('file_2.txt')))</text> </testcode> <stdin> <text></text> </stdin> <expected> <text>status_2</text> </expected> <extra> <text></text> </extra> <display> <text>SHOW</text> </display> </testcase> <file name="fmlie.cpp" path="/" encoding="base64">aW1wb3J0IHJhbmRvbH</file> <file name="idfile.txt" path="/" encoding="base64">TnVA2DQoyICg==</file> </testcases> </question> </quiz>
解决方法
错误原因是tree.find("testcases")返回None——find方法只会查找当前节点的直接子节点,而testcases是question节点的子节点,并非根节点quiz的直接子节点。
有两种修复方式:
- 先找到
question节点,再从中查找testcases - 使用XPath的递归查找语法
.//testcases,直接定位所有层级下的testcases节点
修正后的代码
from lxml import etree tree = etree.parse("d_xml.xml") d='<testcase testtype="0" useasexample="0" hiderestiffail="0" mark="1.0000000" > <testcode> <text>print(solve(' \ 'read_file("file_2.txt")))</text> </testcode><stdin><text></text></stdin><expected><text>status_2</text></expected' \ '><extra><text></text></extra><display><text>SHOW</text> </display></testcase>' # 方式1:先找question,再找testcases question = tree.find("question") contentnav = question.find("testcases") # 方式2:用XPath递归查找(二选一即可) # contentnav = tree.find(".//testcases") contentnav.append(etree.XML(d)) print(etree.tostring(tree)) tree.write('output.xml', pretty_print=True, xml_declaration=True, encoding="utf-8")
运行后即可将新的testcase节点追加到目标testcases节点内部,得到符合预期的XML输出。
内容的提问来源于stack exchange,提问作者rpb
相关产品推荐
相关产品推荐

