Mihir Patel Mihir Patel - 2 months ago 10
Python Question

Python trouble with looping through dictionary and replacing xml attribute

I am having trouble looping through Python list below and replacing xml attribute with all ADSType values.

Python Dictionary

{'ADSType': ['HS11N', 'HS11V'], 'Type': ['Bond', 'Cash']}


XML

I'd like to replace sid values on each line in XML with ADS values.

<req Times="1" action="get" chunklimit="1000" lang="ENU" msg="1" rank="1" rnklst="1" runuf="0">
<flds>
<f end="2016-02-29" freq="m" i="bond(long) hff" sid="abc" start="2016-02-29" />
<f end="2016-02-29" freq="m" i="bond(short) ggg" sid="abc" start="2016-02-29" />
</flds>
<dat>
<r CalculationType="3" ForceCalculate="1" i="123" />
</dat>
</req>


Python Code so far:

data_list = {'ADSType': ['HS11N', 'HS11V'], 'Type': ['Bond', 'Cash']}

xml_file = './test.xml'
tree1 = ET.ElementTree(file=xml_file)
root1 = tree1.getroot()

for x in root1.iter('flds'):
for y in x.iter('f'):
y.set('sid', y.get('sid').replace("123", "abc"))
# print(ET.tostring(root1).decode("UTF-8"))
print "test"
# print(ET.tostring(root1).decode("UTF-8"))
tree1.write("./test.xml")


I am stuck how to iterate over list, extract values, feed in replace method dynamically and update xml. Please help! Following is the desired output I am trying to achieve.

<req Times="1" action="get" chunklimit="1000" lang="ENU" msg="1" rank="1" rnklst="1" runuf="0">
<flds>
<f end="2016-02-29" freq="m" i="bond(long) hff" sid="HS11N" start="2016-02-29" />
<f end="2016-02-29" freq="m" i="bond(short) ggg" sid="HS11V" start="2016-02-29" />
</flds>
<dat>
<r CalculationType="3" ForceCalculate="1" i="123" />
</dat>
</req>

Answer

You don't need to replace anything, just use set with the new value which will overwrite:

from xml.etree import ElementTree as ET
xml_file = "./test.xml"

tree = ET.parse(xml_file)
root1 = tree.getroot()
data_list = {'ADSType':['HS11N', 'HS11V'], 'Type': ['Bond', 'Cash']}

# make iterator so we just call next(sids) to pull each value
sids = iter(data_list['ADSType'])

for flds in root1.iter('flds'):
    for f in flds.iter('f'):
        # set to new value
        f.set("sid", next(sids))
# write
tree.write("./test.xml")

That does exactly what you want although I am not sure how the full dict fits into your question:

test.xml:

In [3]: !cat test.xml
<req Times="1" action="get" chunklimit="1000" lang="ENU" msg="1" rank="1" rnklst="1" runuf="0">
    <flds>
        <f end="2016-02-29" freq="m" i="bond(long) hff" sid="abc" start="2016-02-29" />
        <f end="2016-02-29" freq="m" i="bond(short) ggg" sid="abc" start="2016-02-29" />
    </flds>
    <dat>
        <r CalculationType="3" ForceCalculate="1" i="123" />
    </dat>
</req>

Run the code:

In [4]: from xml.etree import ElementTree as ET

In [5]: xml_file = "./test.xml"

In [6]: tree = ET.parse(xml_file)

In [7]: root1 = tree.getroot()

In [8]: data_list = {'ADSType': iter(['HS11N', 'HS11V']), 'Type': iter(['Bond', 'Cash'])}

In [9]: sids = iter(data_list['ADSType'])

In [10]: for flds in root1.iter('flds'):
   ....:     for f in x.iter('f'):
   ....:              f.set("sid", next(sids))
   ....: tree.write("./test.xml")
   ....: 

New test.xml:

In [11]: !cat test.xml
<req Times="1" action="get" chunklimit="1000" lang="ENU" msg="1" rank="1" rnklst="1" runuf="0">
    <flds>
        <f end="2016-02-29" freq="m" i="bond(long) hff" sid="HS11N" start="2016-02-29" />
        <f end="2016-02-29" freq="m" i="bond(short) ggg" sid="HS11V" start="2016-02-29" />
    </flds>
    <dat>
        <r CalculationType="3" ForceCalculate="1" i="123" />
    </dat>
</req>

If you have multiple f's inside multiple fld's and want to cycle the values, use itertools.cycle in place of iter:

from itertools import cycle
sids = cycle(data_list['ADSType'])