Reputation: 119
So, I'm creating a new subelement with ElementTree where the text of the new node should be a dict value IF dict key of the corresponding value equals the text of another XML node within the same parent Node.
<ns0:scaleType xmlns:ns0="http://someURL.com/">
<scales>
<scale>
<names>
<name id="0">abc</name>
<name id="1" />
</names>
<alternativeExportValues>
</alternativeExportValues>
</scale>
<scale>
<names>
<name id="0">def</name>
<name id="1" />
</names>
<alternativeExportValues>
</alternativeExportValues>
</scale>
</scales>
</ns0:scaleType>
name;value
abc;10012
def;20025
import xml.etree.ElementTree as ET
import csv
csvData = []
with open('myCSV.csv', 'r', encoding="utf8") as f:
reader = csv.reader(f, delimiter=";")
for row in reader:
csvData.append({'name': row[0], 'value': row[1]})
tree = ET.parse('myXml.xml')
root = tree.getroot()
def my_Function():
for p in csvData:
for name in root.findall(".//name[@id='0']"):
text = name.text
if p['name'] == text:
value = p['value']
return value
my_Function()
for elem in root.iter('alternativeExportValues'):
newNode = ET.SubElement(elem, 'alternativeExportValue')
newNode.text =
tree.write("myNewXML.xml", encoding="utf-8")
<ns0:scaleType xmlns:ns0="http://someURL.com/">
<scales>
<scale>
<names>
<name id="0">abc</name>
<name id="1" />
</names>
<alternativeExportValues>
<alternativeExportValue>10012</alternativeExportValue>
</alternativeExportValues>
</scale>
<scale>
<names>
<name id="0">def</name>
<name id="1" />
</names>
<alternativeExportValues>
<alternativeExportValue>20025</alternativeExportValue>
</alternativeExportValues>
</scale>
</scales>
</ns0:scaleType>
I tried to put the for loop that creates the alternativeExportValue
node in my_Function
, but ended up getting the same value in newNode.text
or getting stuck in an endless loop.
As you can see in the expected outcome, I want the dict.value as text for the newly created Node if it matches the <name id="0">
innerText within the same parent <scale>
.
Upvotes: 1
Views: 255
Reputation: 52878
I'm not exactly sure what my_Function
is supposed to be doing, but consider the following logic:
scale
element.alternativeExportValue
element with the "value" value.name
element with the id
attribute value "0" matches the current "name" entry.alternativeExportValue
element.Example...
import xml.etree.ElementTree as ET
import csv
with open('myCSV.csv', 'r', encoding="utf8") as csvfile:
tree = ET.parse('myXml.xml')
for row in csv.DictReader(csvfile, delimiter=";"):
name = row.get("name")
new_aev_elem = ET.Element("alternativeExportValue")
new_aev_elem.text = row.get("value")
for scale in tree.findall(".//scale"):
name0 = scale.find("names/name[@id='0']")
if name0.text == name:
aevs_elem = scale.find("alternativeExportValues")
aevs_elem.append(new_aev_elem)
break
tree.write("myNewXML.xml", encoding="utf-8")
This works but isn't very efficient because you have to process every scale
element that precedes the actual scale
element you want modify.
Even worse, if you remove the break
it would process every scale
element in the XML (for every row of the CSV!).
If you can switch to lxml, you can use a slightly more complex XPath* that will only process the scale
element that needs to be modified...
from lxml import etree
import csv
with open('myCSV.csv', 'r', encoding="utf8") as csvfile:
tree = etree.parse('myXml.xml')
uc = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
lc = "abcdefghijklmnopqrstuvwxyz"
for row in csv.DictReader(csvfile, delimiter=";"):
name = row.get("name").lower()
new_aev_elem = etree.Element("alternativeExportValue")
new_aev_elem.text = row.get("value")
aevs_elem = tree.xpath(f".//scale[translate(names/name[@id='0'],'{uc}','{lc}')='{name}']/alternativeExportValues")[0]
aevs_elem.append(new_aev_elem)
tree.write("myNewXML.xml", encoding="utf-8")
*The XPath support in ElementTree is limited.
Upvotes: 1