Reputation: 5819
Setting values to an element using the objectify
API of the lxml
library, assigns the auto detected pytype
to that element and the required namespaces, by default.
For example, setting the root element:
root = objectify.Element('root')
print(etree.tostring(root, pretty_print=True).decode('utf-8'))
Outputs:
<root xmlns:py="http://codespeak.net/lxml/objectify/pytype"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" py:pytype="TREE"/>
or setting a value to a child element:
child = objectify.SubElement(root, 'child')
root.child = 'value'
print(etree.tostring(root, pretty_print=True).decode('utf-8'))
Outputs:
<root xmlns:py="http://codespeak.net/lxml/objectify/pytype"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" py:pytype="TREE">
<child py:pytype="str">value</child>
</root>
Even using the setattr of ObjectPath:
path = objectify.ObjectPath('root.vader.son')
path.setattr(root, 'Luke')
print(etree.tostring(root, pretty_print=True).decode('utf-8'))
Outputs:
<root xmlns:py="http://codespeak.net/lxml/objectify/pytype"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" py:pytype="TREE">
<child py:pytype="str">value</child>
<vader>
<son py:pytype="str">Luke</son>
</vader>
</root>
There are solutions that remove the pytype
and its namespaces after the element creation, using the deannotate()
function (ex. When using lxml, can the XML be rendered without namespace attributes?, Remove "xmlns:py..." with lxml.objectify). There are no solutions whatsoever that create the element without the pytype
and its namespaces from the beginning. Any ideas on how to do that?
Upvotes: 4
Views: 1036
Reputation: 21
Just another workaround may help you: while setting an element, you can use the _setText() method: vader.son._setText('Luke')
Reason: 'class lxml.objectify.ObjectifiedDataElement', which bases on 'lxml.objectify.ObjectifiedElement' has a private method _setText(). This method is dedicated for use in subclasses only, i. e. lxml.objectify.StringElement-class and doesn´t affect the pytype https://lxml.de/apidoc/lxml.objectify.html
In other words: you can use it as long as you don't change the value type.
Upvotes: 1
Reputation: 5819
In lxml.objectify
, there are two kinds of elements: the tree elements, created by the Element
factory and the data elements, created by DataElement
factory or by the specific data classes, for example StringElement
, IntElement
(for more info, see here). A solution might be to empty the namespaces and the _pytype
argument of the particular element, by assigning it to the empty string and never use direct assignment from literals. To create an element from a literal, you will have to use the DataElement factory. Note that, if you have any particular namespaces, you will have to assign your namespace map, instead of the empty string to the nsmap parameter. There is a problem though. If you want to create a tree element, setting the nsmap
and _pytype
to the empty string, the namespaces and pytype won't be removed. I don't know why. So this solution works only for data elements.
This is how the code would be for the tree you try to build:
root = objectify.Element('root', nsmap='', _pytype='')
# sub elements do not need nsmap or _pytype to be emptied
child = objectify.SubElement(root, 'child')
root.child = objectify.DataElement('value', nsmap='', _pytype='')
path = objectify.ObjectPath('root.vader.son')
path.setattr(root, objectify.DataElement('Luke', nsmap='', _pytype=''))
print(etree.tostring(root, pretty_print=True).decode('utf-8'))
Which outputs:
<root xmlns:py="http://codespeak.net/lxml/objectify/pytype" py:pytype="">
<child>value</child>
<vader>
<son>Luke</son>
</vader>
</root>
Not what we want!
The solution lies in a workaround, using the ElementMaker
factory.
# Create your ElementMaker factory, without annotations.
E = objectify.ElementMaker(annotate=False)
# If you have any namespaces you want to use, assign them to the nsmap
# parameter and assign the default namespace to the namespace parameter.
# E = objectify.ElementMaker(annotate=False, namespace=namespace, nsmap=nsmap)
root = E.root()
print(etree.tostring(root, pretty_print=True))
This outputs:
<root/>
The namespace and pytype problem introduced to the tree elements has been solved. Now we can either assign sub elements or data elements:
objectify.SubElement(root, 'child')
root.child = objectify.DataElement('value', nsmap='', _pytype='')
print(etree.tostring(root, pretty_print=True).decode('utf-8'))
which outputs:
<root>
<child>value</child>
</root>
An example using the setattr()
method is:
root = E.root()
path = objectify.ObjectPath('root.vader.son')
path.setattr(root, objectify.DataElement('Luke', nsmap='', _pytype=''))
# mysteriously, the below line works the same as the above line:
# path.setattr(root, E.whatevername('Luke'))
print(etree.tostring(root, pretty_print=True).decode('utf-8'))
the output of which is:
<root>
<vader>
<son>Luke</son>
</vader>
</root>
Upvotes: 3