How do I add elements in xml based on another element attribute using python?

Viewed 36

I have an xml file which looks like this. There are about 300 "NadClient" elements:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<TipsContents xmlns="http://www.avendasys.com/tipsapiDefs/1.0">
  <TipsHeader exportTime="Wed Apr 07 09:35:24 EDT 2021" version="6.9"/>
  <NadClients>
    <NadClient description="SOMEDESC" name="SOMENAME" coaPort="3799" radsecEnabled="false" coaCapable="false" vendorName="SOMEVENDOR" tacacsSecret="SOMESECRET" radiusSecret="" ipAddress="1.1.1.1/32">
      <NadClientTags tagName="Location" tagValue="SOMETAG"/>
      <NadClientTags tagName="Device Type" tagValue="SOMEVENDOR"/>
    </NadClient>

What I want to do using python is add an extra NadClientTags element before /NadClient, with an attribute copied from the description attribute. So the desired result would be:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<TipsContents xmlns="http://www.avendasys.com/tipsapiDefs/1.0">
  <TipsHeader exportTime="Wed Apr 07 09:35:24 EDT 2021" version="6.9"/>
  <NadClients>
    <NadClient description="SOMEDESC" name="SOMENAME" coaPort="3799" radsecEnabled="false" coaCapable="false" vendorName="SOMEVENDOR" tacacsSecret="SOMESECRET" radiusSecret="" ipAddress="1.1.1.1/32">
      <NadClientTags tagName="Location" tagValue="SOMETAG"/>
      <NadClientTags tagName="Device Type" tagValue="SOMEVENDOR"/>
      <NadClientTags tagName="sysName" tagValue="SOMEDESC"/>
    </NadClient>

I would want the script to read the entire xml file and perform the same change to all 300 NadClient elements.

Ive used the code below to extract the contents of the description attribute of all the NadClient elements but i dont know where to go from there:

import xml.etree.ElementTree as ET

tree = ET.parse("./test.xml")
root = tree.getroot()

for child in root.iter('{http://www.avendasys.com/tipsapiDefs/1.0}NadClient'):
    print(child.attrib)

Can anyone give me some guidance on the best way to approach this?

1 Answers

If you can use lxml instead of elementtree, and assuming the following xml string:

devs = """<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<TipsContents xmlns="http://www.avendasys.com/tipsapiDefs/1.0">
  <TipsHeader exportTime="Wed Apr 07 09:35:24 EDT 2021" version="6.9"/>
  <NadClients>
    <NadClient description="SOMEDESC" name="SOMENAME" coaPort="3799" radsecEnabled="false"
               coaCapable="false" vendorName="SOMEVENDOR" tacacsSecret="SOMESECRET" radiusSecret="" ipAddress="1.1.1.1/32">
      <NadClientTags tagName="Location" tagValue="SOMETAG"/>
      <NadClientTags tagName="Device Type" tagValue="SOMEVENDOR"/>

    </NadClient>
    <NadClient description="SOMEOTHERDESC" name="SOMENAME2" coaPort="3799" radsecEnabled="false" coaCapable="false" vendorName="SOMEVENDOR"
               tacacsSecret="SOMESECRET" radiusSecret="" ipAddress="1.1.1.1/32">
      <NadClientTags tagName="Location" tagValue="SOMETAG"/>
      <NadClientTags tagName="Device Type" tagValue="SOMEVENDOR"/>
 
    </NadClient>
   </NadClients> 
 </TipsContents>"""

You can try something like this:

from lxml import etree
doc = etree.XML(devs.encode())
ns = {'xxx':'http://www.avendasys.com/tipsapiDefs/1.0'}
for nc in doc.xpath('//xxx:NadClient',namespaces=ns):
    new_tag = nc.xpath('./@description')[0]
    new_elem = etree.fromstring(f'<NadClientTags tagName="sysName" tagValue="{new_tag}"/>')
    nc.insert(2,new_elem)
print(etree.tostring(doc).decode())

The output should be what you are looking for.

Related