Generating xml with LXML - attribute namespace - python

I need to generate xml which looks like this:
<definitions xmlns:ex="http://www.example1.org" xmlns="http://www.example2.org">
<typeRef xmlns:ns2="xyz">text</typeRef>
</definitions>
My code looks as follows:
class XMLNamespaces:
ex = 'http://www.example1.org'
xmlns = 'http://www.example2.org'
root = Element('definitions', xmlns='http://www.example2.org', nsmap = {'ex':XMLNamespaces.ex})
type_ref = SubElement(root, 'typeRef')
type_ref.attrib[QName(XMLNamespaces.xmlns, 'ns2')] = 'xyz'
type_ref.text = 'text'
tree = ElementTree(root)
tree.write('filename.xml', pretty_print=True)
The result looks like:
<definitions xmlns:ex="http://www.example1.org" xmlns="http://www.example2.org">
<typeRef xmlns:ns0="http://www.example2.org" ns0:ns2="xyz">text</typeRef>
</definitions>
So here is my question:
How to make attribute look like xmlns:ns2="xyz" instead of xmlns:ns0="http://www.example2.org" ns0:ns2="xyz"?

Simply run same process as your opening element where you defined the namespace dictionary with nsmap argument. Notice the added variable in your class object:
from lxml.etree import *
class XMLNamespaces:
ex = 'http://www.example1.org'
xmlns = 'http://www.example2.org'
xyz = 'xyz'
root = Element('definitions', xmlns='http://www.example2.org', nsmap={'ex':XMLNamespaces.ex})
type_ref = SubElement(root, 'typeRef', nsmap={'ns2':XMLNamespaces.xyz})
type_ref.text = 'text'
tree = ElementTree(root)
tree.write('filename.xml', pretty_print=True)
# <definitions xmlns:ex="http://www.example1.org" xmlns="http://www.example2.org">
# <typeRef xmlns:ns2="xyz">text</typeRef>
# </definitions>

Related

How to add Subelements and its in xml

I have a xml file which has subelements:-
.......
.......
<EnabledFeatureListForUsers>
<FeatureEntitlementDetail>
<UserName>xyz#xyz.com</UserName>
<FeatureList>
<FeatureDetail>
<FeatureId>X</FeatureId>
</FeatureDetail>
</FeatureList>
</FeatureEntitlementDetail>
</EnabledFeatureListForUsers>
.....
.....
I want to add a new sub element FeatureEntitlementDetail with all its subelements/children like username, Feature List, Feature Detail, Feature Id. I tried using SubElement function, but it only adds FeatureEntitlementDetail />. The code which I used was :-
import xml.etree.ElementTree as ET
filename = "XYZ.xml"
xmlTree = ET.parse(filename)
root = xmlTree.getroot()
for element in root.iter('EnabledFeatureListForUsers'):
ET.SubElement(element,"FeatureEntitlementDetail")
Any help is appreciated.
See below
import xml.etree.ElementTree as ET
xml = """
<EnabledFeatureListForUsers>
<FeatureEntitlementDetail>
<UserName>xyz#xyz.com</UserName>
<FeatureList>
<FeatureDetail>
<FeatureId>X</FeatureId>
</FeatureDetail>
</FeatureList>
</FeatureEntitlementDetail>
</EnabledFeatureListForUsers>
"""
root = ET.fromstring(xml)
fed = ET.SubElement(root,'FeatureEntitlementDetail')
un = ET.SubElement(fed,'UserName')
un.text = 'abc.zz.net'
fl = ET.SubElement(fed,'FeatureList')
df = ET.SubElement(fl,'FeatureDetail')
fi = ET.SubElement(df,'FeatureId')
fi.text = 'Z'
ET.dump(root)
output
<?xml version="1.0" encoding="UTF-8"?>
<EnabledFeatureListForUsers>
<FeatureEntitlementDetail>
<UserName>xyz#xyz.com</UserName>
<FeatureList>
<FeatureDetail>
<FeatureId>X</FeatureId>
</FeatureDetail>
</FeatureList>
</FeatureEntitlementDetail>
<FeatureEntitlementDetail>
<UserName>abc.zz.net</UserName>
<FeatureList>
<FeatureDetail>
<FeatureId>Z</FeatureId>
</FeatureDetail>
</FeatureList>
</FeatureEntitlementDetail>
</EnabledFeatureListForUsers>

How to add namespace in existing xml file

Sample xml:
<abcd>
... Many contents here ...
</abcd>
I want to change sample file like below:
<abcd xmlns="urn:myname" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="urn:myname myname.xsd">
.... many contents here
</abcd>
So, my code is below, but when I printed out the document the result is same with the input file.
attr_qname = etree.QName("http://www.w3.org/2001/XMLSchema-instance", "schemaLocation")
nsmap = {None: "urn:myname", 'xsi':"http://www.w3.org/2001/XMLSchema-instance"}
document = etree.parse(self.fname)
root = document.getroot()
root = etree.Element('abcd', {attr_qname: 'urn:myname myname.xsd'}, nsmap=nsmap)
print("Parsed : ", etree.tostring(document, pretty_print=True).decode())
How can I add the namespace and print out?
in your code you're just creating a new abcd and don't do anything with it
try this
attr_qname = etree.QName("http://www.w3.org/2001/XMLSchema-instance", "schemaLocation")
nsmap = {None: "urn:myname", 'xsi':"http://www.w3.org/2001/XMLSchema-instance"}
document = etree.parse(self.fname)
root = document.getroot()
abcd = root.find('abcd')
abcd.set('xmlns', "urn:myname")
abcd.set(attr_qname, "urn:myname myname.xsd")
print("Parsed : ", etree.tostring(root, pretty_print=True).decode())

Python ElementTree adding a child

I have an xml file which looks like this:
<keyboard>
</keyboard>
I want to make it look like this:
<keyboard>
<keybind key="W-c-a"><action name="Execute"><command>sudo shutdown now</command></action></keybind>
</keyboard>
I have a function to add this which has parameters that will change the key and the command. Is this possible to do? If yes, how can I do this?
(The function):
def add_keybinding(self, keys, whatToExec):
keybinding = "<keybind key=\"%s\"><action name=\"Execute\"><command>%s</command><action></keybind>" % (keys, whatToExec)
f = open("/etc/xdg/openbox/rc.xml", "a")
try:
# I want to append the keybinding variable to the <keyboard>
except IOError as e:
print(e)
From the doc, you can try the following:
def add_keybinding(keys, whatToExec, filename):
keybind = ET.Element('keybind')
keybind.set("key", keys)
action = ET.SubElement(keybind, 'action')
action.set("name", "Execute")
command = ET.SubElement(action, 'command')
command.text = whatToExec
tree = ET.parse(filename)
tree.getroot().append(keybind)
tree.write(filename)
Explanation:
Create the keybind tag using xml.etree.ElementTree.Element : keybind = ET.Element('keybind')
Set a property using set: keybind.set("key", keys)
Create the action tag as a sub element of keybind using
xml.etree.ElementTree.SubElement: action = ET.SubElement(keybind, 'action')
Set the property as at step 2: action.set("name", "Execute")
Create command tag: action.set("name", "Execute")
Set command tag content using .text: command.text = whatToExec
Read file using xml.etree.ElementTree.parse: tree = ET.parse(filename)
Append keybind tag to the doc root element using append*
Export new xml to file using write
Full example:
import xml.etree.ElementTree as ET
from xml.dom import minidom
def add_keybinding(keys, whatToExec, filename):
keybind = ET.Element('keybind')
keybind.set("key", keys)
action = ET.SubElement(keybind, 'action')
action.set("name", "Execute")
command = ET.SubElement(action, 'command')
command.text = whatToExec
tree = ET.parse(filename)
tree.getroot().append(keybind)
tree.write(filename)
return tree
def prettify(elem):
rough_string = ET.tostring(elem, 'utf-8')
return minidom.parseString(rough_string).toprettyxml(indent=" ")
filename = "test.xml"
for i in range(3):
tree = add_keybinding(str(i), "whatToExec " + str(i), filename)
print(prettify(tree.getroot()))
Output:
<?xml version="1.0" ?>
<keyboard>
<keybind key="0">
<action name="Execute">
<command>whatToExec 0</command>
</action>
</keybind>
<keybind key="1">
<action name="Execute">
<command>whatToExec 1</command>
</action>
</keybind>
<keybind key="2">
<action name="Execute">
<command>whatToExec 2</command>
</action>
</keybind>
</keyboard>

How should I parse this xml string in python?

My XML string is -
xmlData = """<SMSResponse xmlns="http://example.com" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Cancelled>false</Cancelled>
<MessageID>00000000-0000-0000-0000-000000000000</MessageID>
<Queued>false</Queued>
<SMSError>NoError</SMSError>
<SMSIncomingMessages i:nil="true"/>
<Sent>false</Sent>
<SentDateTime>0001-01-01T00:00:00</SentDateTime>
</SMSResponse>"""
I am trying to parse and get the values of tags - Cancelled, MessageId, SMSError, etc. I am using python's Elementtree library. So far, I have tried things like -
root = ET.fromstring(xmlData)
print root.find('Sent') // gives None
for child in root:
print chil.find('MessageId') // also gives None
Although, I am able to print the tags with -
for child in root:
print child.tag
//child.tag for the tag Cancelled is - {http://example.com}Cancelled
and their respective values with -
for child in root:
print child.text
How do I get something like -
print child.Queued // will print false
Like in PHP we can access them with the root -
$xml = simplexml_load_string($data);
$status = $xml->SMSError;
Your document has a namespace on it, you need to include the namespace when searching:
root = ET.fromstring(xmlData)
print root.find('{http://example.com}Sent',)
print root.find('{http://example.com}MessageID')
output:
<Element '{http://example.com}Sent' at 0x1043e0690>
<Element '{http://example.com}MessageID' at 0x1043e0350>
The find() and findall() methods also take a namespace map; you can search for a arbitrary prefix, and the prefix will be looked up in that map, to save typing:
nsmap = {'n': 'http://example.com'}
print root.find('n:Sent', namespaces=nsmap)
print root.find('n:MessageID', namespaces=nsmap)
If you're set on Python standard XML libraries, you could use something like this:
root = ET.fromstring(xmlData)
namespace = 'http://example.com'
def query(tree, nodename):
return tree.find('{{{ex}}}{nodename}'.format(ex=namespace, nodename=nodename))
queued = query(root, 'Queued')
print queued.text
You can create a dictionary and directly get values out of it...
tree = ET.fromstring(xmlData)
root = {}
for child in tree:
root[child.tag.split("}")[1]] = child.text
print root["Queued"]
With lxml.etree:
In [8]: import lxml.etree as et
In [9]: doc=et.fromstring(xmlData)
In [10]: ns={'n':'http://example.com'}
In [11]: doc.xpath('n:Queued/text()',namespaces=ns)
Out[11]: ['false']
With elementtree you can do:
import xml.etree.ElementTree as ET
root=ET.fromstring(xmlData)
ns={'n':'http://example.com'}
root.find('n:Queued',namespaces=ns).text
Out[13]: 'false'

How to insert a child subelement in Python ElementTree

My xml :-
<users>
</users>
i need to just append a child element :-
<users>
<user name="blabla" age="blabla" ><group>blabla</group>
</users>
My code giving some error :(
import xml.etree.ElementTree as ET
doc = ET.parse("users.xml")
root_node = doc.find("users")
child = ET.SubElement(root_node, "user")
child.set("username","srquery")
group = ET.SubElement(child,"group")
group.text = "fresher"
tree = ET.ElementTree(root_node)
tree.write("users.xml")
I missed out "append" , but i don't have idea where to add that. Thanks in advance.
Change this line
root_node = doc.find("users")
...to this line
root_node = doc.getroot()
The key takeaway here is that doc is already a reference to the root node, and is accessed with getroot(). doc.find('users') will not return anything, since users is not a child of the root, it's the root itself.
A slightlly modified version to explain what happens:
root = ET.fromstring('<users></users>') # same as your doc=ET.parse(...).find(...), btw. doc=root
el = ET.Element('group') # creating a new element/xml-node
root.append(el) # and adding it to the root
ET.tostring(root)
>>> '<users><group /></users>'
el.text = "fresher" # adding your text
ET.tostring(root)
>>> '<users><group>fresher</group></users>'

Categories