Python write result of XML ElementTree findall to a file - python

I want to write a python code to extract some data from a source XML file and write to a new file. My source file is like this:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header/>
<soapenv:Body>
<SessionID xmlns="http://www.niku.com/xog">12345</SessionID>
<QueryResult xmlns="http://www.niku.com/xog/Query" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Records>
<Record>
<id>1</id>
<date_start>2020-10-04T00:00:00</date_start>
<date_end>2020-10-10T00:00:00</date_end>
<name>Payne, Max</name>
</Record>
<Record>
<id>2</id>
<date_start>2020-10-04T00:00:00</date_start>
<date_end>2020-10-10T00:00:00</date_end>
<name>Reno, Jean</name>
</Record>
</Records>
</QueryResult>
</soapenv:Body>
</soapenv:Envelope>
I want to write the following output to a new xml file.
<Records>
<Record>
<id>1</id>
<date_start>2020-10-04T00:00:00</date_start>
<date_end>2020-10-10T00:00:00</date_end>
<name>Payne, Max</name>
</Record>
<Record>
<id>2</id>
<date_start>2020-10-04T00:00:00</date_start>
<date_end>2020-10-10T00:00:00</date_end>
<name>Reno, Jean</name>
</Record>
</Records>
I was able to get following results from this code.
import xml.etree.ElementTree as ET
tree = ET.parse('my_file.xml')
root = tree.getroot()
for xtag in root.findall('.//{http://www.niku.com/xog/Query}Record'):
print(xtag)
Result:
<Element '{http://www.niku.com/xog/Query}Record' at 0x00000216BA69B778>
<Element '{http://www.niku.com/xog/Query}Record' at 0x00000216BA6A3228>
Can anyone help me to complete my requirement?

In your case print(xtag) prints the xtag object and not a string. For that you would need to convert the object to a string using the tree's tostring() method. Also, it seems you are looking to get the whole <Records> block instead of the individual <Record> elements; for this you don't need a loop.
import xml.etree.ElementTree as ET
tree = ET.parse('test.xml')
root = tree.getroot()
records = root.find('.//{http://www.niku.com/xog/Query}Records')
print(ET.tostring(records).decode("utf-8"))
Output
<ns0:Records xmlns:ns0="http://www.niku.com/xog/Query">
<ns0:Record>
<ns0:id>1</ns0:id>
<ns0:date_start>2020-10-04T00:00:00</ns0:date_start>
<ns0:date_end>2020-10-10T00:00:00</ns0:date_end>
<ns0:name>Payne, Max</ns0:name>
</ns0:Record>
<ns0:Record>
<ns0:id>2</ns0:id>
<ns0:date_start>2020-10-04T00:00:00</ns0:date_start>
<ns0:date_end>2020-10-10T00:00:00</ns0:date_end>
<ns0:name>Reno, Jean</ns0:name>
</ns0:Record>
</ns0:Records>
You could also use the lxml module, which gives a slightly different output.
from lxml import etree
tree = etree.parse('test.xml')
root = tree.getroot()
records = root.find('.//{http://www.niku.com/xog/Query}Records')
print(etree.tostring(records).decode("utf-8"))
Output
<Records xmlns="http://www.niku.com/xog/Query" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<Record>
<id>1</id>
<date_start>2020-10-04T00:00:00</date_start>
<date_end>2020-10-10T00:00:00</date_end>
<name>Payne, Max</name>
</Record>
<Record>
<id>2</id>
<date_start>2020-10-04T00:00:00</date_start>
<date_end>2020-10-10T00:00:00</date_end>
<name>Reno, Jean</name>
</Record>
</Records>

Related

How to copy xml entire elements, attributes and data to new xml file with specific id using python

Below is my sample xml source file
<?xml version="1.0" encoding="UTF-8"?>
<catalog xmlns="http://www.sample.com/xml/catalog" catalog-id="sample-catalog">
<product product-id="214146430">
<online-flag>false</online-flag>
<online-flag site-id="sample_ae">false</online-flag>
<available-flag>true</available-flag>
<searchable-flag>true</searchable-flag>
<tax-class-id>standard</tax-class-id>
<page-attributes/>
<custom-attributes>
<custom-attribute attribute-id="adultsize">L</custom-attribute>
</custom-attributes>
</product>
<product product-id="214146123">
<online-flag>false</online-flag>
<online-flag site-id="sample_ae">false</online-flag>
<available-flag>true</available-flag>
<searchable-flag>true</searchable-flag>
<tax-class-id>standard</tax-class-id>
<page-attributes/>
<custom-attributes>
<custom-attribute attribute-id="adultsize">L</custom-attribute>
</custom-attributes>
</product>
</catalog>
I want to copy only product id 214146430 to
New xml file and it should look like below
<?xml version="1.0" encoding="UTF-8"?>
<catalog xmlns="http://www.sample.com/xml/catalog" catalog-id="sample-catalog">
<product product-id="214146430">
<online-flag>false</online-flag>
<online-flag site-id="sample_ae">false</online-flag>
<available-flag>true</available-flag>
<searchable-flag>true</searchable-flag>
<tax-class-id>standard</tax-class-id>
<page-attributes/>
<custom-attributes>
<custom-attribute attribute-id="adultsize">L</custom-attribute>
</custom-attributes>
</product>
</catalog>
I am currently using xml.etree.ElementTree and xml.dom but no luck
but it is just copying the entire xml which is is not expected
Below is my python code
import xml.etree.ElementTree as ET
tree = ET.parse('Development/product_data_parser/emporio-imoprt-test.xml')
root = tree.getroot()
print(ET.tostring(root, encoding='utf8').decode('utf8'))
Thank so much in advance for your help
You could do it like this although this solution might be specific to your use-case:-
import xml.etree.ElementTree as ET
import re
# A list of product IDs that you want to keep
keep = ['214146430']
# Figure out the namespace (used in tag matching later)
def getnamespace(root):
m = re.match(r'\{.*\}', root.tag)
return m.group(0) if m is not None else ''
tree = ET.parse('Development/product_data_parser/emporio-imoprt-test.xml')
root = tree.getroot()
namespace = getnamespace(root)
for elem in root.findall(f'{namespace}product'):
if elem.attrib['product-id'] not in keep:
root.remove(elem)
print(ET.tostring(root, encoding='utf8').decode('utf8'))

Parsing XML in Python with ElementTree - findall()

I'm using the documentation here to try to get only the values (address , mask ) for certain elements.
This is an example of the structure of my XML:
<?xml version="1.0" ?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:52622325-b136-40cf-bc36-85332e25b6f3" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
<data>
<native xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-native">
<interface>
<GigabitEthernet>
<name>1</name>
<ip>
<address>
<primary>
<address>192.168.40.30</address>
<mask>255.255.255.0</mask>
</primary>
</address>
</ip>
<logging>
<event>
<link-status/>
</event>
</logging>
<mop>
<enabled>false</enabled>
<sysid>false</sysid>
</mop>
<negotiation xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-ethernet">
<auto>true</auto>
</negotiation>
</GigabitEthernet>
<GigabitEthernet>
<name>2</name>
<ip>
<address>
<primary>
<address>10.10.10.1</address>
<mask>255.255.255.0</mask>
</primary>
</address>
</ip>
<logging>
<event>
<link-status/>
</event>
</logging>
<mop>
<enabled>false</enabled>
<sysid>false</sysid>
</mop>
<negotiation xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-ethernet">
<auto>true</auto>
</negotiation>
</GigabitEthernet>
<GigabitEthernet>
<name>3</name>
<ip>
<address>
<primary>
<address>30.30.30.1</address>
<mask>255.255.255.0</mask>
</primary>
</address>
</ip>
<logging>
<event>
<link-status/>
</event>
</logging>
<mop>
<enabled>false</enabled>
<sysid>false</sysid>
</mop>
<negotiation xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-ethernet">
<auto>true</auto>
</negotiation>
</GigabitEthernet>
<GigabitEthernet>
<name>4</name>
<logging>
<event>
<link-status/>
</event>
</logging>
<mop>
<enabled>false</enabled>
<sysid>false</sysid>
</mop>
<negotiation xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-ethernet">
<auto>true</auto>
</negotiation>
</GigabitEthernet>
</interface>
</native>
</data>
Working off this example in the documentation, I've tried something like this:
import xml.etree.ElementTree as ET
tree = ET.parse("C:\\Users\\Redha\\Documents\\test_network\\interface123.xml")
root = tree.getroot()
for i in root.findall('native'):
print(i.tag)
But it returns nothing . I've tried other things to no success. Any ideas? All advice appreciated. Thank you!
Consider using namespaces when referencing XML elements:
import xml.etree.ElementTree as ET
# declare XML namespaces
namespaces = {'native': 'http://cisco.com/ns/yang/Cisco-IOS-XE-native'}
tree = ET.parse("C:\\Users\\Redha\\Documents\\test_network\\interface123.xml")
root = tree.getroot()
# call findall() using previously created namespaces map
for i in root.findall('.//native:native', namespaces):
print(i.tag)

XML to check conditions and ouput tags, PYTHON

I have a xml file as shown below:
<records>
<record>
<device_serial_number>PAD203146024</device_serial_number>
<device_serial_number_2>203146024</device_serial_number_2>
<docsis_mac>6412695EB9BB</docsis_mac>
</record>
<record>
<device_serial_number>PAD206024</device_serial_number>
<device_serial_number_2>20324</device_serial_number_2>
<docsis_mac>6412BB</docsis_mac>
</record>
</records>
I read this xml file using ETree in python and works well, now I want to check if tag has text starting with "PAD" if it has then output it as below:
Expected output
<record>
<device_serial_number>PAD203146024</device_serial_number>
<device_serial_number_2>203146024</device_serial_number_2>
<docsis_mac>6412695EB9BB</docsis_mac>
</record>
import xml.etree.ElementTree as ET
recs=[ET.tostring(c) for c in record if c.find('device_serial_number').text[:3]=='PAD']
for rec in recs:
print(rec)
where record above is record element in the for loop as shown in your question. This should return your records only if 'device_serial_number' starts with PAD as below:-
<record>
<device_serial_number>PAD203146024</device_serial_number>
<device_serial_number_2>203146024</device_serial_number_2>
<docsis_mac>6412695EB9BB</docsis_mac>
</record>
<record>
<device_serial_number>PAD206024</device_serial_number>
<device_serial_number_2>20324</device_serial_number_2>
<docsis_mac>6412BB</docsis_mac>
</record>
If any of the record elements do not have device_serial_number as a child then I would use the below:
from xml.etree import ElementTree as ET
from xml.dom.minidom import parseString
tree = ET.parse('C:\\Use\\CO.xml')
root = tree.getroot()
for record in root.findall('records'):
recs=[c for c in t if c.find('device_serial_number')!=None and c.find('device_serial_number').text[:3]=='PAD']
for rec in recs:
print(parseString(ET.tostring(rec)).toprettyxml(''))

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.

how to change a node value in python

<?xml version="1.0"?>
<info>
</tags>
</tags>
<area>
<media>
<options>
<name>Jaipur</name>
</options>
</media>
</area>
</info>
i am totaly new in python, here is my xml file and i want to edit element value at run time in python
it means I want to change the <name>Jaipur</name> to <name>Mumbai</name>
First, the example is not valid xml. You can use xml.etree that comes included:
from xml.etree import ElementTree as et
xmlstr="""\
<?xml version="1.0"?>
<area>
<media>
<options>
<name>Jaipur</name>
</options>
</media>
</area>"""
doc=et.fromstring(xmlstr)
doc.find('.//name').text='Mumbai'
print et.tostring(doc)
output:
<area>
<media>
<options>
<name>Mumbai</name>
</options>
</media>
</area>

Categories