I have a script which I want to use to create Write Files to update a serial number on a device.
It does a folder/file path check -> Copies the Read.xml and Creates a Write.xml (Copy) -> Obtains the Serial Number to update using .getroot -> Then I want to update/write a new value to this Write.xml.
How do I change the value at root_IM1[1][2].text?
#Import File Path Verification
import os
import sys
import os.path
import time
from pathlib import Path
import shutil
#XML
import xml.etree.ElementTree as ET
#Shell Command
import subprocess
#Enter Serial No.
NAC_Ser = 012345678
#Check Folder Structure
NAC_FilePath = Path(f"C:\Folder\\{(NAC_Ser}")
folp1_2 = Path(f"C:\Folder\{(NAC_Ser}\Folder1")
folp2_2 = Path(f"C:\Folder\{(NAC_Ser)}\Folder1\Folder2_1")
folp3_2 = Path(f"C:\Folder\{(NAC_Ser)}\Folder1\Folder2_2")
if (NAC_FilePath.exists() == True) and (folp1_2.exists() == True) and (folp2_2.exists() == True) and (folp3_2.exists() == True):
print("\n")
print("Folder Check Passed\n")
else:
print("\n")
print("Failed Folder Check")
#Declare Filepaths Global
global fp1_2
#Check File Exist
fp1_2 = Path(f"C:\Folder\{(NAC_Ser)}\Folder1\Folder2_1\ReadIm1.xml")
if (fp1_2.exists() == True):
print("File Check Passed\n")
print("XML File Imported\n")
else:
print("Failed File Check")
# Copy Read Files
# Rename Read Files (Copy) to Write Files
# Write New Values to Write.XML
print("\n")
print("Creating Write XML Files...")
# Source Filepath for Copy -> Destination Filepath -> New File Name
print("\n")
print("Source FilePath to Copy:")
print(fp1_2)
print("\n")
print("New Destination FilePath & New FileName:")
dest_fp1 = Path(f"C:\Folder\{(NAC_Ser)}\Folder1\Folder2_1\WriteIM1.xml")
print(dest_fp1)
shutil.copy2(fp1_2, dest_fp1) # Copy the file
#XML Query
tree_IM1 = ET.parse(dest_fp1)
root_IM1 = tree_IM1.getroot()
#IM1 -> IIB PCB Serial (Current)
IM1_IIBSerial = (f"{root_IM1[1][2].text}")
print(IM1_IIBSerial)
#IM1 -> IIB PCB Serial (New Updated)
IM1_IIBSerial_New = 12345
print(IM1_IIBSerial_New)
You can assign the new value to root_IM1[1][2].text
before writing the new file.
import xml.etree.ElementTree as ET
tree_IM1 = ET.parse(dest_fp1)
root_IM1 = tree_IM1.getroot()
root_IM1[1][2].text = "12345"
tree_IM1.write(dest_fp1)
This method is potentially prone to errors if the order of the tags were to change. A safer approach would be to use a XPath expression to specify the tag which is to be updated.
To demonstrate here is a toy XML file, data.xml
which uses the same structure to that outlined in the question. The root tag users
has two child elements, each of which has three child elements. We want to change the city
of the second user
to Paris
.
<users>
<user>
<name>Jane Doe</name>
<age>25</age>
<city>New York</city>
</user>
<user>
<name>John Smith</name>
<age>27</age>
<city>London</city>
</user>
</users>
We could use the same code as provided above.
import xml.etree.ElementTree as ET
xml_file_path = "data.xml"
tree = ET.parse(xml_file_path)
root = tree_IM1.getroot()
root[1][2].text = "Paris"
tree.write(xml_file_path)
A more robust solution is possible using XPath to find the element to update. Notice that the XML is parsed using a different module, this is because the xml.etree.ElementTree
module does not support XPath expressions like lxml
does.
from lxml import etree
# Path to XML file
xml_file_path = "data.xml"
# Parse the XML file using lxml
tree = etree.parse(xml_file_path)
# Define the XPath to find the city in the second user element
xpath_expression = "/users/user[2]/city"
# Use XPath to find the city element
city_elements = tree.xpath(xpath_expression)
# Check if city_elements is not empty (i.e., city is found)
if city_elements:
# Select the first city element (assuming there's only one)
city_element = city_elements[0]
# Update the text of the city element
city_element.text = "Paris"
# Write the updated XML back to the file
tree.write(xml_file_path)
print("City updated successfully for the second user.")
else:
print("City not found for the second user.")
Resulting XML file:
<users>
<user>
<name>Jane Doe</name>
<age>25</age>
<city>New York</city>
</user>
<user>
<name>John Smith</name>
<age>27</age>
<city>Paris</city>
</user>
</users>
For completeness, here is a way to emulate the XPath solution using functions available in xml.etree.ElementTree
:
import xml.etree.ElementTree as ET
# Path to XML file
xml_file_path = "data.xml"
# Parse the XML file
tree = ET.parse(xml_file_path)
root = tree.getroot()
# Define the XPath-like expression to find the city in the second user element
user_elements = root.findall("user")
if len(user_elements) > 1:
second_user = user_elements[1] # Get the second user
city_element = second_user.find("city") # Find the city element under the second user
# Check if city_element is found
if city_element is not None:
# Update the text of the city element
city_element.text = "Paris"
# Write the updated XML back to the file
tree.write(xml_file_path)
print("City updated successfully for the second user.")
else:
print("City not found for the second user.")
else:
print("Second user not found in the XML data.")