Python lxml objectify: Strange behaviour when changing an elements value
<p>IÂ´m new to stack overflow, so "hi" to everbody and I hope someone can help me with my question...</p>

<p>Recently I start to play around a bit with lxml.objectify and stumble over the following behavior, which I found to be strange.
If I just create a little xml string like this:</p>

<pre><code>from lxml import objectify 

objroot = objectify.fromstring("&lt;root&gt;somerootvalue&lt;child1/&gt;&lt;child2/&gt;&lt;child3/&gt;&lt;child4&gt;&lt;subchild1/&gt;&lt;subchild2/&gt;&lt;/child4&gt;&lt;child5&gt;&lt;subchild1/&gt;&lt;subchild2/&gt;&lt;/child5&gt;&lt;/root&gt;")
objroot.child4 = True
objroot.child4.subchild1 = "Foo"
objroot.child4.subchild2 = "Bar"
print(objroot.child4.subchild1,objroot.child4.subchild2,objroot.child4)
</code></pre>

<p>The output is just: Foo Bar true</p>

<p>If I change the value/text of the elements then by:</p>

<pre><code>from lxml import objectify 

objroot = objectify.fromstring("&lt;root&gt;somerootvalue&lt;child1/&gt;&lt;child2/&gt;&lt;child3/&gt;&lt;child4&gt;&lt;subchild1/&gt;&lt;subchild2/&gt;&lt;/child4&gt;&lt;child5&gt;&lt;subchild1/&gt;&lt;subchild2/&gt;&lt;/child5&gt;&lt;/root&gt;")
objroot.child4 = True
objroot.child4.subchild1 = "Foo"
objroot.child4.subchild2 = "Bar"
print(objroot.child4.subchild1,objroot.child4.subchild2,objroot.child4)
objroot.child4 = False
objroot.child4.subchild1 = "Foo"
objroot.child4.subchild2 = "Bar"
print(objroot.child4.subchild1,objroot.child4.subchild2,objroot.child4)
</code></pre>

<p>The output is as expected:
    Foo Bar true,
    Foo Baz false</p>

<p>But if I just change the value of objroot.child4 and call the print statement, IÂ´ve got the following error:</p>

<pre><code>from lxml import objectify 

objroot = objectify.fromstring("&lt;root&gt;somerootvalue&lt;child1/&gt;&lt;child2/&gt;&lt;child3/&gt;&lt;child4&gt;&lt;subchild1/&gt;&lt;subchild2/&gt;&lt;/child4&gt;&lt;child5&gt;&lt;subchild1/&gt;&lt;subchild2/&gt;&lt;/child5&gt;&lt;/root&gt;")
objroot.child4 = True
objroot.child4.subchild1 = "Foo"
objroot.child4.subchild2 = "Bar"
print(objroot.child4.subchild1,objroot.child4.subchild2,objroot.child4)
objroot.child4 = False
objroot.child4.subchild1 = "Foo"
objroot.child4.subchild2 = "Bar"
print(objroot.child4.subchild1,objroot.child4.subchild2,objroot.child4)
objroot.child4 = True
print(objroot.child4.subchild1,objroot.child4.subchild2,objroot.child4)
File "src\lxml\lxml.objectify.pyx", line 450, in lxml.objectify._lookupChildOrRaise (src\lxml\lxml.objectify.c:6586)
AttributeError: no such child: subchild1
</code></pre>

<p>While I expect the last output to be "Foo Bar true", IÂ´ve got the "no such child error".
So it seems that the remaining part of the tree behind child4 has been cut of? Is tha a desired behavior and if yes how can I change the text of an element in the middle of the tree without cutting the rest of?</p>

<p>Thank you for your help!</p>

<p>It is actually not that strange of behavior once we dig into it a bit.  Using <code>root.element.subelement....</code> is not performing as you might assume it is.  We can use <code>etree</code> to print out the state of the xml tree and check the structure.</p>

<pre><code>from lxml import objectify, etree

objroot = objectify.fromstring("&lt;root&gt;somerootvalue&lt;child1/&gt;&lt;child2/&gt;&lt;child3/&gt;&lt;child4&gt;&lt;subchild1/&gt;&lt;subchild2/&gt;&lt;/child4&gt;&lt;child5&gt;&lt;subchild1/&gt;&lt;subchild2/&gt;&lt;/child5&gt;&lt;/root&gt;")

print(etree.tostring(objroot, pretty_print=True)

#output:
&lt;root&gt;somerootvalue
    &lt;child1/&gt;
    &lt;child2/&gt;
    &lt;child3/&gt;
    &lt;child4&gt;&lt;subchild1/&gt;&lt;subchild2/&gt;&lt;/child4&gt;
    &lt;child5&gt;&lt;subchild1/&gt;&lt;subchild2/&gt;&lt;/child5&gt;
&lt;/root&gt;
</code></pre>

<p>This looks correct.  So what happens when we call <code>objroot.child4 = True</code>?  The API allows you to do this, but it does not just add the text.  Rather, it replaces everything that was under <code>child4</code> with the text.  So the subelements get dropped.  We can check using:</p>

<pre><code>objroot.child4 = True
print(etree.tostring(objroot, pretty_print=True)

#output:
&lt;root&gt;somerootvalue
    &lt;child1/&gt;
    &lt;child2/&gt;
    &lt;child3/&gt;
    &lt;child4 xmlns:py="..." py:pytype="bool"&gt;true&lt;/child4&gt;
    &lt;child5&gt;&lt;subchild1/&gt;&lt;subchild2/&gt;&lt;/child5&gt;
&lt;/root&gt;
</code></pre>

<p>So it has set the value of <code>child4</code> to <code>True</code>, but it has dropped the subelements.  After that, when you set the values of the subelements using:</p>

<pre><code>objroot.child4.subchild1 = "Foo"
objroot.child4.subchild2 = "Bar"
</code></pre>

<p>It actually creates each subelement under <code>child4</code> and then sets the value on the fly.  </p>

python python-3.x attributeerror lxml.objectify