Issue navigating to xml element with same name, changing different attribute - python

If have an xml file that looks like:
<?xml version="1.0"?>
-<apple view_filter="simple" version="1" format="1">
<apples fruit_id="3" type="red" name="american">
<basket version="1" type="6" pieces="12" expiration="12">
<fruit_type colour="000" fruit_type="0x" weight="32">
</basket>
</apples>
</apple>
For the element fruit_type="0x", I want to be able to use python code to navigate to that element and change the text (0x) of it's attribute. I also want to do the same for 'colour' and 'weight'.
How can I do this because when I try to navigate to fruit_type, I end up changing the fruit_type (first element) not the one that is fruit_type = '0x'?

The code that does exactly what I want is:
import xml.etree.ElementTree as ET
parent = ET.parse("d:\\untitled\\note.xml")
root = parent.getroot()
for nodes in root.getchildren() :
for subNodes in nodes.getchildren() :
for mynode in subNodes.iterfind('basket'):
print("##### Before Change of attributes ########### \n")
print(ET.tostring(mynode))
print("\n ##### After Change of attributes ###########\n")
mynode.set('fruit_type', '0234')
mynode.set('colour', '999')
mynode.set('weight', '45')
print(ET.tostring(mynode))

Here is a sample code how you can change the attributes of Fruit_type:
Sample Code
import xml.etree.ElementTree as ET
parent = ET.parse("d:\\untitled\\note.xml")
root = parent.getroot()
for nodes in root.getchildren() :
for subNodes in nodes.getchildren() :
for mynode in subNodes.getchildren():
print("##### Before Change of attributes ########### \n")
print(ET.tostring(mynode))
print("\n ##### After Change of attributes ###########\n")
mynode.set('fruit_type', '0234')
mynode.set('colour', '999')
mynode.set('weight', '45')
print(ET.tostring(mynode))
Output
##### Before Change of attributes ###########
b'<fruit_type colour="000" fruit_type="0x" weight="32">\n </fruit_type>\n '
##### After Change of attributes ###########
b'<fruit_type colour="999" fruit_type="0234" weight="45">\n </fruit_type>\n '
hope this helps

Related

How to write multiple XML parameters correctly in python-docx

Trying to change the width of an existing Word table using XML. I need to write to the XML parameters that is to get the code: <w:tblW w:w="5000" w:type="pct"/> But it does not work. See below how it turns out. Please tell me why this happens? How to do it right?
import docx
from docx.oxml.table import CT_Row, CT_Tc
from docx.oxml import OxmlElement
from docx.oxml.ns import qn
from docx import Document
doc = docx.Document('example.docx')
# all tables via XML
for table in doc.tables:
table.style = 'Normal Table'
tbl = table._tbl # get xml element in table
tblPr = tbl.tblPr # We get an xml element containing the style and width
print('============================ before ==============================')
print(table._tbl.xml) # Output the entire xml of the table
# Setting the table width to 100%. To do this, look at the xml example:
# <w:tblW w:w="5000" w:type="pct"/> - this is size 5000 = 100%, and type pct = %
#
tblW = OxmlElement('w:tblW')
w = OxmlElement('w:w')
w.set(qn('w:w'), '5000')
type = OxmlElement('w:type')
type.set(qn('w:type'), 'pct')
tblW.append(w)
tblW.append(type)
tblPr.append(tblW) # Adding the recorded results to the elements
print('============================ after ==============================')
print(table._tbl.xml) # Output the entire xml of the table
doc.save('restyled.docx')
We get the following results:
============================ before ==============================
...
<w:tblPr>
<w:tblW w:w="8880" w:type="dxa"/>
<w:tblCellMar>
<w:top w:w="15" w:type="dxa"/>
<w:left w:w="15" w:type="dxa"/>
<w:bottom w:w="15" w:type="dxa"/>
<w:right w:w="15" w:type="dxa"/>
</w:tblCellMar>
<w:tblLook w:val="04A0" w:firstRow="1" w:lastRow="0" w:firstColumn="1" w:lastColumn="0" w:noHBand="0" w:noVBand="1"/>
</w:tblPr>
...
============================ after ==============================
...
<w:tblPr>
...
<w:tblW>
<w:w w:w="5000"/>
<w:type w:type="pct"/>
</w:tblW>
</w:tblPr>
...
There should have been a result:
...
<w:tblPr>
...
<w:tblW w:w="5000" w:type="pct"/>
</w:tblPr>
...
Just add:
w.set(qn('w:type'), 'pct')
Instead of these lines:
tblW.append(w)
tblW.append(type)
tblPr.append(tblW) # Adding the recorded results to the elements
w:type is an attribute, which is added using the set() method. The append() method is used to add a child element.

Element search by path with attribute name and Replace values in xml using Python

How can i give complete path with attribute value to element and replace element value in xml using Python
For example: I have xml file like this:
<bw>
<bwprocesses>
<bwprocess name="Business_Processes/Source_Processes/Receive Batch Message.process">
<starter>Receive Batch Message</starter>
</bwprocess>
<bwprocess name="Business_Processes/Source_Processes/Type Subscriber.process">
<starter>Receive Trade Message</starter>
</bwprocess>
<bwprocess name="Business_Processes/Source_Processes/Message Router.process">
<starter>Message JMS Queue Receiver</starter>
</bwprocess>
</bwprocesses>
</bw>
and want to update xml by giving complete element path(with attribute value) which needs to be updated.
input : bwprocesses/ bwprocess [Business_Processes/Source_Processes/Receive Batch Message.process]/starter = new massage (new value should be updated in place of 'Receive Batch Message' in .xml)
I tried search example using xml.etree.ElementTree but couldn't find any useful info.Tried below code to search element but getting error .Please help on it.
import xml.etree.ElementTree as ET
tree = ET.parse('a.xml')
root = tree.getroot()
for e in root.findall('bwprocesses/bwprocess[#name='Business_Processes/Source_Processes/Receive Batch Message.process']/starter'):
print e.text
See below
import xml.etree.ElementTree as ET
xml = '''<bw>
<bwprocesses>
<bwprocess name="Business_Processes/Source_Processes/Receive Batch Message.process">
<starter>Receive Batch Message</starter>
</bwprocess>
<bwprocess name="Business_Processes/Source_Processes/Type Subscriber.process">
<starter>Receive Trade Message</starter>
</bwprocess>
<bwprocess name="Business_Processes/Source_Processes/Message Router.process">
<starter>Message JMS Queue Receiver</starter>
</bwprocess>
</bwprocesses>
</bw>'''
root = ET.fromstring(xml)
for e in root.findall("bwprocesses/bwprocess[#name='Business_Processes/Source_Processes/Receive Batch Message.process']/starter"):
print('Before: {}'.format(e.text))
e.text = 'Any text you want'
for e in root.findall("bwprocesses/bwprocess[#name='Business_Processes/Source_Processes/Receive Batch Message.process']/starter"):
print('After: {}'.format(e.text))

How to insert values using python in xml file

I am programming novice and have just started learning python
below is my xml file:
<Build_details>
<Release number="1902">
<Build number="260">
<OMS>
<Build_path>ST_OMS_V1810_B340</Build_path>
<Pc_version>8041.30.01</Pc_version>
</OMS>
<OMNI>
<Build_path>ST_OMNI_V1810_B340</Build_path>
</OMNI>
</Build>
</Release>
<Release number="1810">
<Build number="230">
<OMS>
<Build_path>ST_OMS_909908</Build_path>
<Pc_version>8031.25.65</Pc_version>
</OMS>
<OMNI>
<Build_path>ST_OMNI_798798789789</Build_path>
</OMNI>
</Build>
</Release>
<Release number="1806">
<Build number="300">
<OMS>
<Build_path>ST_OMS_V18102_B300</Build_path>
<Pc_version>8041.30.01</Pc_version>
</OMS>
<OMNI>
<Build_path>ST_OMNI_V18102_B300</Build_path>
</OMNI>
</Build>
</Release>
</Build_details>
How can i insert below chunk of data by asking release no to user and insert below it :
<Build number="230">
<OMS>
<Build_path>ST_OMS_909908</Build_path>
<Pc_version>8031.25.65</Pc_version>
</OMS>
<OMNI>
<Build_path>ST_OMNI_798798789789</Build_path>
</OMNI>
</Build>
I need to search a particular release and then add details to it.Please help
i am not unable to traverse xml to find a particular release
I'm not able to add my comment because of less Reputations .
go through this link Reading XML file and fetching its attributes value in Python
Here is the solution using python inbuilt library xml,
You will have to find the release element first and then create a new build element and append to the release element.
import xml.etree.ElementTree as ET
if __name__ == "__main__":
release_number = input("Enter the release number\n").strip()
tree = ET.ElementTree(file="Build.xml") # Original XML File
root = tree.getroot()
for elem in root.iterfind('.//Release'):
# Find the release element
if elem.attrib['number'] == release_number:
# Create new Build Element
build_elem = ET.Element("Build", {"number": "123"})
# OMS element
oms_elem = ET.Element("OMS")
build_path_elem = ET.Element("Build_path")
build_path_elem.text = "ST_OMS_909908"
pc_version_elem = ET.Element("Pc_version")
pc_version_elem.text = "8031.25.65"
oms_elem.append(build_path_elem)
oms_elem.append(pc_version_elem)
omni_elem = ET.Element("OMNI")
build_path_omni_elem = ET.Element("Build_path")
build_path_omni_elem.text = "ST_OMNI_798798789789"
omni_elem.append(build_path_omni_elem)
build_elem.append(oms_elem)
build_elem.append(omni_elem)
elem.append(build_elem)
# Write to file
tree.write("Build_new.xml") # After adding the new element

Python XML findall does not work

I am trying to use findall to select on some xml elements, but i can't get any results.
import xml.etree.ElementTree as ET
import sys
storefront = sys.argv[1]
xmlFileName = 'promotions{0}.xml'
xmlFile = xmlFileName.format(storefront)
csvFileName = 'hrz{0}.csv'
csvFile = csvFileName.format(storefront)
ET.register_namespace('', "http://www.demandware.com/xml/impex/promotion/2008-01-31")
tree = ET.parse(xmlFile)
root = tree.getroot()
print('------------------Generate test-------------\n')
csv = open(csvFile,'w')
n = 0
for child in root.findall('campaign'):
print(child.attrib['campaign-id'])
print(n)
n+=1
The XML looks something like this:
<?xml version="1.0" encoding="UTF-8"?>
<promotions xmlns="http://www.demandware.com/xml/impex/promotion/2008-01-31">
<campaign campaign-id="10off-310781">
<enabled-flag>true</enabled-flag>
<campaign-scope>
<applicable-online/>
</campaign-scope>
<customer-groups match-mode="any">
<customer-group group-id="Everyone"/>
</customer-groups>
</campaign>
<campaign campaign-id="MNT-deals">
<enabled-flag>true</enabled-flag>
<campaign-scope>
<applicable-online/>
</campaign-scope>
<start-date>2017-07-03T22:00:00.000Z</start-date>
<end-date>2017-07-31T22:00:00.000Z</end-date>
<customer-groups match-mode="any">
<customer-group group-id="Everyone"/>
</customer-groups>
</campaign>
<campaign campaign-id="black-friday">
<enabled-flag>true</enabled-flag>
<campaign-scope>
<applicable-online/>
</campaign-scope>
<start-date>2017-11-23T23:00:00.000Z</start-date>
<end-date>2017-11-24T23:00:00.000Z</end-date>
<customer-groups match-mode="any">
<customer-group group-id="Everyone"/>
</customer-groups>
<custom-attributes>
<custom-attribute attribute-id="expires_date">2017-11-29</custom-attribute>
</custom-attributes>
</campaign>
<promotion-campaign-assignment promotion-id="winter17-new-bubble" campaign-id="winter17-new-bubble">
<qualifiers match-mode="any">
<customer-groups/>
<source-codes/>
<coupons/>
</qualifiers>
<rank>100</rank>
</promotion-campaign-assignment>
<promotion-campaign-assignment promotion-id="xmas" campaign-id="xmas">
<qualifiers match-mode="any">
<customer-groups/>
<source-codes/>
<coupons/>
</qualifiers>
</promotion-campaign-assignment>
</promotions>
Any ideas what i am doing wrong?
I have tried different solutions that i found on stackoverflow but nothing seems to work for me(from the things i have tried).
The list is empty.
Sorry if it is something very obvious i am new to python.
As mentioned here by #MartijnPieters, etree's .findall uses the namespaces argument while the .register_namespace() is used for xml output of the tree. Therefore, consider mapping the default namespace with an explicit prefix. Below uses doc but can even be cosmin.
Additionally, consider with and enumerate() even the csv module as better handlers for your print and CSV outputs.
import csv
...
root = tree.getroot()
print('------------------Generate test-------------\n')
with open(csvFile, 'w') as f:
c = csv.writer(f, lineterminator='\n')
for n, child in enumerate(root.findall('doc:campaign', namespaces={'doc':'http://www.demandware.com/xml/impex/promotion/2008-01-31'})):
print(child.attrib['campaign-id'])
print(n)
c.writerow([child.attrib['campaign-id']])
# ------------------Generate test-------------
# 10off-310781
# 0
# MNT-deals
# 1
# black-friday
# 2

how to get file names and paths based on a given attribute in parent tag

I want to change the below code to get file_names and file_paths only when fastboot="true" attribute is present in the parent tag,I provided the current output and expected ouput,can anyone provide guidance on how to do it?
import sys
import os
import string
from xml.dom import minidom
if __name__ == '__main__':
meta_contents = minidom.parse("fast.xml")
builds_flat = meta_contents.getElementsByTagName("builds_flat")[0]
build_nodes = builds_flat.getElementsByTagName("build")
for build in build_nodes:
bid_name = build.getElementsByTagName("name")[0]
print "Checking if this is cnss related image... : \n"+bid_name.firstChild.data
if (bid_name.firstChild.data == 'apps'):
file_names = build.getElementsByTagName("file_name")
file_paths = build.getElementsByTagName("file_path")
print "now files paths...\n"
for fn,fp in zip(file_names,file_paths):
if (not fp.firstChild.nodeValue.endswith('/')):
fp.firstChild.nodeValue = fp.firstChild.nodeValue + '/'
full_path = fp.firstChild.nodeValue+fn.firstChild.nodeValue
print "file-to-copy: "+full_path
break
INPUT XML:-
<builds_flat>
<build>
<name>apps</name>
<file_ref ignore="true" minimized="true">
<file_name>adb.exe</file_name>
<file_path>LINUX/android/vendor/qcom/proprietary/usb/host/windows/prebuilt/</file_path>
</file_ref>
<file_ref ignore="true" minimized="true">
<file_name>system.img</file_name>
<file_path>LINUX/android/out/target/product/msmcobalt/secondary-boot/</file_path>
</file_ref>
<download_file cmm_file_var="APPS_BINARY" fastboot_rumi="boot" fastboot="true" minimized="true">
<file_name>boot.img</file_name>
<file_path>LINUX/android/out/target/product/msmcobalt/</file_path>
</download_file>
<download_file sparse_image_path="true" fastboot_rumi="abl" fastboot="true" minimized="true">
<file_name>abl.elf</file_name>
<file_path>LINUX/android/out/target/product/msmcobalt/</file_path>
</download_file>
</build>
</builds_flat>
OUTPUT:-
...............
now files paths...
file-to-copy: LINUX/android/vendor/qcom/proprietary/usb/host/windows/prebuilt/adb.exe
file-to-copy: LINUX/android/out/target/product/msmcobalt/secondary-boot/system.img
file-to-copy: LINUX/android/out/target/product/msmcobalt/boot.img
file-to-copy: LINUX/android/out/target/product/msmcobalt/abl.elf
EXPECTED OUT:-
now files paths...
........
file-to-copy: LINUX/android/out/target/product/msmcobalt/boot.img
file-to-copy: LINUX/android/out/target/product/msmcobalt/abl.elf
Something rather quick and dirty that comes to mind is using the fact that only the download_file elements have the fastboot attribute, right? If that's the case, you could always get the children of type download_file and filter the ones whose fastboot attribute is not "true":
import os
from xml.dom import minidom
if __name__ == '__main__':
meta_contents = minidom.parse("fast.xml")
for elem in meta_contents.getElementsByTagName('download_file'):
if elem.getAttribute('fastboot') == "true":
path = elem.getElementsByTagName('file_path')[0].firstChild.nodeValue
file_name = elem.getElementsByTagName('file_name')[0].firstChild.nodeValue
print os.path.join(path, file_name)
With the sample you provided that outputs:
$ python ./stack_034.py
LINUX/android/out/target/product/msmcobalt/boot.img
LINUX/android/out/target/product/msmcobalt/abl.elf
Needless to say... since there's no .xsd file (nor that it'd matter with the minidom, though) you only get strings (no type safety) and this only applies to the structure shown in the example (you probably would like to add some extra checks there, is what I mean)
EDIT:
As per the comment in this answer:
To get the elements within the <build> that contains a <name> attribute with value apps, you can: Find that <name> tag (the one whose value is the string apps), then move to the parent node (which will put you in the build element) and then proceed as mentioned above:
if __name__ == '__main__':
meta_contents = minidom.parse("fast.xml")
for elem in meta_contents.getElementsByTagName('name'):
if elem.firstChild.nodeValue == "apps":
apps_build = elem.parentNode
for elem in apps_build.getElementsByTagName('download_file'):
if elem.getAttribute('fastboot') == "true":
path = elem.getElementsByTagName('file_path')[0].firstChild.nodeValue
file_name = elem.getElementsByTagName('file_name')[0].firstChild.nodeValue
print os.path.join(path, file_name)

Categories