XML header getting removed after processing with elementtree - python

i have an xml file and i used Elementtree to add a new tag to the xml file.My xml file before processing is as follows
<?xml version="1.0" encoding="utf-8"?>
<PackageInfo xmlns="http://someurlpackage">
<data ID="http://someurldata1">data1</data >
<data ID="http://someurldata2">data2</data >
<data ID="http://someurldata3">data3</data >
</PackageInfo>
I used following python code to add a new data tag and write it to my xml file
tree = ET.ElementTree(xmlFile)
root = tree.getroot()
elem= ET.Element('data')
elem.attrib['ID']="http://someurldata4"
elem.text='data4'
root[1].append(elem)
tree = ET.ElementTree(root)
tree.write(xmlFile)
But the resultant xml file have <?xml version="1.0" encoding="utf-8"?> absent and the file looks as below
<PackageInfo xmlns="http://someurlpackage">
<data ID="http://someurldata1">data1</data >
<data ID="http://someurldata2">data2</data >
<data ID="http://someurldata3">data3</data >
</PackageInfo>
Is there any way to include the xml header rather than hardcoding the line

It looks like you need optional arguments to the write method to output the declaration.
http://docs.python.org/library/xml.etree.elementtree.html#elementtree-elementtree-objects
tree.write(xmlfile,xml_declaration=True)
I'm afraid I'm not that familiar with xml.etree.ElementTree and it's variation between python releases.
Here's it working with lxml.etree:
>>> from lxml import etree
>>> sample = """<?xml version="1.0" encoding="utf-8"?>
... <PackageInfo xmlns="http://someurlpackage">
... <data ID="http://someurldata1">data1</data >
... <data ID="http://someurldata2">data2</data >
... <data ID="http://someurldata3">data3</data >
... </PackageInfo>"""
>>>
>>> doc = etree.XML(sample)
>>> data = doc.makeelement("data")
>>> data.attrib['ID'] = 'http://someurldata4'
>>> data.text = 'data4'
>>> doc.append(data)
>>> etree.tostring(doc,xml_declaration=True)
'<?xml version=\'1.0\' encoding=\'ASCII\'?>\n<PackageInfo xmlns="http://someurlpackage">\n<data ID="http://someurldata1">data1</data>\n<data ID="http://someurldata2">data2</data>\n<data ID="http://someurldata3">data3</data>\n<data ID="http://someurldata4">data4</data></PackageInfo>'
>>> etree.tostring(doc,xml_declaration=True,encoding='utf-8')
'<?xml version=\'1.0\' encoding=\'utf-8\'?>\n<PackageInfo xmlns="http://someurlpackage">\n<data ID="http://someurldata1">data1</data>\n<data ID="http://someurldata2">data2</data>\n<data ID="http://someurldata3">data3</data>\n<data ID="http://someurldata4">data4</data></PackageInfo>'

try this:::
tree.write(xmlFile, encoding="utf-8")

If you are using python <=2.6
There is no xml_declaration parameter in ElementTree.write()
def write(self, file, encoding="us-ascii"):
def _write(self, file,node, encoding, namespaces):
You can use lxml.etree
install lxml
sample here:
from lxml import etree
document = etree.Element('outer')
node = etree.SubElement(document, 'inner')
print(etree.tostring(document, xml_declaration=True))
BTW:
I find that it is not necessary to write the xml_declaration
Is the XML declaration node mandatory?
There is no XML declaration necessary for a document to be
successfully readable, since there are defaults for both version and
encoding (1.0 and UTF-8, respectively).
At least,it works even if AndroidManifest.xml does not have an xml_declaration
I have tried :-)

Related

How to load xml file with specifc paragraph by xml in Python?

I have a xml file and its structure like that,
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<book>
<toc> <tocdiv pagenum="564">
<title>9thmemo</title>
<tocdiv pagenum="588">
<title>b</title>
</tocdiv>
</tocdiv></toc>
<chapter><title>9thmemo</title>
<para>...</para>
<para>...</para></chapter>
<chapter>...</chapter>
<chapter>...</chapter>
</book>
There are several chapters in the <book>...</book>, and each chapter has a title, I only want to read all content of this chapter,"9thmemo"(not others)
I tried to read by following code:
from xml.dom import minidom
filename = "result.xml"
file = minidom.parse(filename)
chapters = file.getElementsByTagName('chapter')
for i in range(10):
print(chapters[i])
I only get the address of each chapter...
if I add some sub-element like chapters[i].title, it shows cannot find this attribute
I only want to read all content of this chapter,"9thmemo"(not others)
The problem with the code is that it does not try to locate the specific 'chapter' while the answer code uses xpath in order to locate it.
Try the below
import xml.etree.ElementTree as ET
xml = '''<?xml version="1.0" encoding="UTF-8"?>
<book>
<toc>
<tocdiv pagenum="564">
<title>9thmemo</title>
<tocdiv pagenum="588">
<title>b</title>
</tocdiv>
</tocdiv>
</toc>
<chapter>
<title>9thmemo</title>
<para>A</para>
<para>B</para>
</chapter>
<chapter>...</chapter>
<chapter>...</chapter>
</book>'''
root = ET.fromstring(xml)
chapter = root.find('.//chapter/[title="9thmemo"]')
para_data = ','.join(p.text for p in chapter.findall('para'))
print(para_data)
output
A,B

parsing xml with Python minidom

I'm trying to parse elements from .xml file with Python minidom library, but it doesn't seem to work. It's returning "IndexError list out of range". Perhaps I'm using incorrect method/library for the job. Please suggest how to do this. Thanks
from xml.dom import minidom
doc = minidom.parse('/path/to/file/runParameters.xml')
docs = doc.getElementsByTagName('RunParameters')
for el in docs:
cloud = el.getElementsByTagName("EnableCloud")
print(cloud[0].firstChild.nodeValue)
Here is what the structure of the file looks like
<?xml version="1.0"?>
<RunParameters xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<EnableCloud>false</EnableCloud>
<RunParametersVersion>MiSeq</RunParametersVersion>
<CopyManifests>true</CopyManifests>
<FlowcellRFIDTag>
<SerialNumber>000000000-AG01C</SerialNumber>
<PartNumber>17772</PartNumber>
<ExpirationDate>2016-04-10T00:00:00</ExpirationDate>
</FlowcellRFIDTag>
</RunParameters>
Using ElementTree
import xml.etree.ElementTree as ET
xml = '''<?xml version="1.0"?>
<RunParameters xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<EnableCloud>false</EnableCloud>
<RunParametersVersion>MiSeq</RunParametersVersion>
<CopyManifests>true</CopyManifests>
<FlowcellRFIDTag>
<SerialNumber>000000000-AG01C</SerialNumber>
<PartNumber>17772</PartNumber>
<ExpirationDate>2016-04-10T00:00:00</ExpirationDate>
</FlowcellRFIDTag>
</RunParameters>'''
root = ET.fromstring(xml)
print(root.find('.//EnableCloud').text)
output
false
This code works for me. Please try it on your system:
xx = '''
<?xml version="1.0"?>
<RunParameters xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<EnableCloud>false</EnableCloud>
<RunParametersVersion>MiSeq</RunParametersVersion>
<CopyManifests>true</CopyManifests>
<FlowcellRFIDTag>
<SerialNumber>000000000-AG01C</SerialNumber>
<PartNumber>17772</PartNumber>
<ExpirationDate>2016-04-10T00:00:00</ExpirationDate>
</FlowcellRFIDTag>
</RunParameters>
'''.strip()
with open('test2.xml','w') as f:
f.write(xx)
from xml.dom import minidom
doc = minidom.parse('test2.xml')
docs = doc.getElementsByTagName('RunParameters')
for el in docs:
cloud = el.getElementsByTagName("EnableCloud")
print(cloud[0].firstChild.nodeValue)
Output
false

XML file generating unwanted data

I have tried writing few things to xml file after reading it from a different xml file, everything works smoothly but there are few unwanted tags coming inside the xml file which i generate as output.
Here is what I have tried
from xml.etree import ElementTree as ET
from xml.dom.minidom import getDOMImplementation
from xml.dom.minidom import parseString
tree = ET.parse('C:\\Users\\ca33.xml')
root = tree.getroot()
impl = getDOMImplementation()
#print(root)
header = [root.find('header')]
for h in header:
h1=(parseString(ET.tostring(h)).toprettyxml(''))
#print(h1)
commands = root.findall(".//records//")
recs=[c for c in commands if c.find('soc_id')!=None and c.find('soc_id').text[:9]=='000001051']
bb=""
for rec in recs:
aa=(parseString(ET.tostring(rec)).toprettyxml(''))
bb=bb+aa
#print(bb)
newdoc = impl.createDocument(None, "file"+h1+bb, None)
newdoc.writexml(open('data.xml', 'w'),'\n'.join([line for line in newdoc.toprettyxml(indent=' '*2).split('\n') if line.strip()]))
I get the output data.xml file as.
<?xml version="1.0" ?><?xml version="1.0" ?>
<file<?xml version="1.0" ?>
<header>
<number_of_records>41</number_of_records>
</header>
<?xml version="1.0" ?>
<record>
<soc_id>00000105139E3B82</soc_id>
</record>
<?xml version="1.0" ?>
<soc_id>00000105139E3640</soc_id>
</record>
<?xml version="1.0" ?>
<header>
<number_of_records>41</number_of_records>
So you can see that many tags of <?xml version="1.0" ?> is being generated everywhere and in the last it again starts writing the data from first but leaves a 2 line spacing
So, what I understand is that you are trying to read a xml file at first place and then you are trying to write the same data into a different file.
In this process you are running into problems
from xml.etree import ElementTree as ET
tree = ET.parse('C:\\Users\\ca33.xml')
root = tree.getroot()
for header_ex in root.findall('header'):
h = [ET.tostring(c) for c in header_ex]
str_header=str(h)
for record_ex in root.findall('records'):
r = [ET.tostring(c) for c in record if c.find('soc_id')!=None and c.find('soc_id').text[:9]=='000001051']
for rec in r:
str_rec=str(rec)
with open("output.xml","w") as f:
f.write("<?xml version='1.0' encoding='ASCII' standalone='yes'?>")
f.write("<file>"+"<header>"+str_header+"</header>")
f.close()
Since you have not posted any random data, I assume it to be the way you had posted in question.I assume that record is a tag and it has something more or many sub/child tags inside it and that's the reason for me to loop twice over it.
And also stop using unnecessary imports in your code.

keep original xml declaration when editing with python

my original xml file looks like this:
<?xml version="1.0" encoding="utf-8"?>
<foo/>
and I want to change it to
<?xml version="1.0" encoding="utf-8"?>
<foo>
<bar>confusing dev</bar>
</foo>
I am using xml.etree.ElementTree as suggested by this tutorial
with open('file.xml','r+b') as f:
tree = etree.parse(f)
f.seek(0,0)
tree.write(f,xml_declaration=True)# default argument: encoding="us-ascii"
this outputs
<?xml version='1.0' encoding='us-ascii'?>
<foo/>
But how do I get the encoding of file.xml at runtime and pass it as an argument to tree.write or is there a better way to edit xml in python? I just want to change some Element.text but keep the declaration and namespace unchanged.

Python: Appending children to an already created XML file's root using XML DOM

I'm totally new to XML and I'm stuck on how to append children to a root node of an already exisitng XML file using Python and XML DOM. Right now I have this script to create an output file:
from xml.dom.minidom import Document
doc = Document()
root_node = doc.createElement("notes") # Root
doc.appendChild(root_node)
object_node = doc.createElement("data") # Child
root_node.appendChild(object_node)
object_node.setAttribute("a_version", "something_v001.0001.ma") # Set attributes
object_node.setAttribute("b_user", "Me")
object_node.setAttribute("c_comment", "comment about file")
xml_file = open("C:/Temp/file_notes.xml", "w") # Append
xml_file.write(doc.toprettyxml())
xml_file.close()
This gives me an output file that looks like this:
<?xml version="1.0" ?>
<notes>
<data a_version="Me" b_user="something_v001.0001.ma" c_comment="comment about file"/>
</notes>
I'd like to append future data to this file so it will look something like this after 2 additional versions:
<?xml version="1.0" ?>
<notes>
<data a_version="something_v001.0001.ma" b_user="Me" c_comment="comment about file"/>
<data a_version="something_v001.0002.ma" b_user="You" c_comment="minor save"/>
<data a_version="something_v002.0003.ma" b_user="Them" c_comment="major save"/>
</notes>
But every attempt I make at appending data comes out like this:
<?xml version="1.0" ?>
<notes>
<data a_version="Me" b_user="something_v001.0001.ma" c_comment="comment about file"/>
</notes>
<?xml version="1.0" ?>
<notes>
<data a_version="Me" b_user="something_v001.0001.ma" c_comment="comment about file"/>
</notes>
<?xml version="1.0" ?>
<notes>
<data a_version="Me" b_user="something_v001.0001.ma" c_comment="comment about file"/>
</notes>
If anyone has any alternative methods to accomplish this task by using ElementTree that would be appreciated as well. There seem to be a lot more resources, but I'm not sure how to implement the solution with Maya. Thanks!
You haven't shown us any code demonstrating "every attempt I make at appending data". But never mind, here is how you can use ElementTree to append new elements to an existing XML file.
from xml.etree import ElementTree as ET
from xml.dom import minidom
# Assume that we have an existing XML document with one "data" child
doc = ET.parse("file_notes.xml")
root = doc.getroot()
# Create 2 new "data" elements
data1 = ET.Element("data", {"a_version": "something_v001.0002.ma",
"b_user": "You",
"c_comment": "minor save"})
data2 = ET.Element("data", {"a_version": "something_v001.0003.ma",
"b_user": "Them",
"c_comment": "major save"})
# Append the new "data" elements to the root element of the XML document
root.append(data1)
root.append(data2)
# Now we have a new well-formed XML document. It is not very nicely formatted...
out = ET.tostring(root)
# ...so we'll use minidom to make the output a little prettier
dom = minidom.parseString(out)
print dom.toprettyxml()
Output:
<?xml version="1.0" ?>
<notes>
<data a_version="Me" b_user="something_v001.0001.ma" c_comment="comment about file"/>
<data a_version="something_v001.0002.ma" b_user="You" c_comment="minor save"/>
<data a_version="something_v001.0003.ma" b_user="Them" c_comment="major save"/>
</notes>
ElementTree does not have a built-in pretty-printer, so we use minidom for that. The output contains some superfluous whitespace, but it is better than what ElementTree can provide.

Categories