I have case where need to copy xml element multiple time based on data received from csv file. we csv records in list of dictionaries and we are taking the length of csv file and also number of times we need to copy the same element into single head tag. Case: if we have len_csv records 14 and number of elements needs copy 4 into single head tag i.e 14/4=3.5 means 4 root tags to fit all 14 tags into all 4 tags means last one root tag should only have 2 tags. Sample xml:
<head_tag>
<root xmlns:ns="http://schemas.org/">
<body>
<values>
<ns:value>
<a>HI1</a>
</ns:value>
</values>
</body>
</root>
</head_tag>
Expected Output:-
<head_tag>
<root xmlns:ns="http://schemas.org/">
<body>
<values>
<ns:value>
<a>HI1</a>
<a>HI1</a>
<a>HI1</a>
<a>HI1</a>
</ns:value>
</values>
</body>
</root>
<root xmlns:ns="http://schemas.org/">
<body>
<values>
<ns:value>
<a>HI1</a>
<a>HI1</a>
<a>HI1</a>
<a>HI1</a>
</ns:value>
</values>
</body>
</root>
<root xmlns:ns="http://schemas.org/">
<body>
<values>
<ns:value>
<a>HI1</a>
<a>HI1</a>
<a>HI1</a>
<a>HI1</a>
</ns:value>
</values>
</body>
</root>
<root xmlns:ns="http://schemas.org/">
<body>
<values>
<ns:value>
<a>HI1</a>
<a>HI1</a>
</ns:value>
</values>
</body>
</root>
</head_tag>
What i am getting:
<head_tag>
<root xmlns:ns="http://schemas.org/">
<body>
<values>
<ns:value>
<a>HI1</a>
<a>HI1</a>
<a>HI1</a>
<a>HI1</a>
</ns:value>
</values>
</body>
</root>
<root xmlns:ns="http://schemas.org/">
<body>
<values>
<ns:value>
<a>HI1</a>
<a>HI1</a>
<a>HI1</a>
<a>HI1</a>
</ns:value>
</values>
</body>
</root>
<root xmlns:ns="http://schemas.org/">
<body>
<values>
<ns:value>
<a>HI1</a>
<a>HI1</a>
<a>HI1</a>
<a>HI1</a>
</ns:value>
</values>
</body>
</root>
<root xmlns:ns="http://schemas.org/">
<body>
<values>
<ns:value>
<a>HI1</a>
<a>HI1</a>
<a>HI1</a>
<a>HI1</a>
</ns:value>
</values>
</body>
</root>
</head_tag>
Now we should have tag 14 times and number copies 4 in each root tag i.e. 3 and last root tag should only have 2 copies of tag as condition mentioned above.
Code:
from lxml import etree
from copy import deepcopy
src='abc.xml'
tree = etree.parse(src)
#Get the root element
root=tree.getroot()
#Get the namespace
nsmap = {k if k is not None else 'default':v for k,v in root.nsmap.items()}
check_value= lambda x: int(x) if x==int(x) else int(x)+1
#let suppose length of csv_records is 14 which having list of dict
len_csv=14
def copy_element(tag_name, len_csv, num_of_copies, batches):
#Copying the head tag as par the calculation in this case 4
for name in root:
for i in range(num_of_copies-1):
new_name = deepcopy(name)
name.addnext(new_name)
k=batches-1
#Copying the tag for xpath given in this case <a> tag as per the calculation
for name in root.findall(tag_name, namespaces=nsmap):
get_last_val=len_csv%num_of_copies
for i in range(num_of_copies):
if k==num_of_copies-1 and get_last_val!=0:
batches=get_last_val
if len_csv!=1:
for j in range(batches-1):
if len_csv<=1:
break
new_name = deepcopy(name)
name.addnext(new_name)
len_csv-=batches
break
k-=1
#Caluclation how many head tags required for batch process
head_tag_required=len_csv/4
copy_element('root/body/values/ns:value/a', len_csv, check_value(head_tag_required),4)
tree.write(src)
The above code is creating 4 tags correctly but tag creating 4 times in each head tag. I have tried to break the findall() condition but none of them are working.
So, My questions are:
- Is there any way we can fix the above issue.? How we can copy the same element multiple times for given xpath that can satisfy above conditions
- also the xpath we are passing is not working, it's unable to find the element due to namespace, How can we fix this issue? the output shown above is element doesn't have any namespace..