Home > Blockchain >  How to append new node in xml with Python?
How to append new node in xml with Python?

Time:11-09

The objective was to append a node within the node testcases using the lxml append function.

However, the compiler return the following error

AttributeError: 'NoneType' object has no attribute 'append'

But, append the new node into the super node such as question does not produce any error. But as expected, this does not produce the intended output.

May I know how to address this issue?

The code to reproduce the above issue is

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")

The d_xml.xml content is a below or downloadable via the link

<?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>

The expected output is as below

<?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>
      <testcases>
        <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>

CodePudding user response:

find() method finds the first child of the context element, in this case, root element. But testcases is not a direct child so it returns None.

Two solutions:
Find question and then testcases out of it.

contentnav = doc.find("question").find("testcases")

Use Xpath

contentnav = doc.xpath("//testcases")[0]

CodePudding user response:

You are almost there; try:

d=etree.fromstring('<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>')

dest = tree.xpath('//question//testcases//testcase')[-1]
#this makes sure d is inserted as the last testcase

dest.addnext(d)
print(etree.tostring(tree).decode())        
  • Related