0

I have an xml file.

<?xml version='1.0' encoding='utf-8'?>
<systemdata>
     <process>
          <number code="hsfg" class="hgdgf" tool="gagfa">
               <value type="string" />
               <value type="None" />
          </number>
          <!-- ID -->
          <id code="hsfg" class="gfdg" tool="fadg">
               <value type="string" />
               <value type="None" />
          </id>
     </process>
</systemdata>

I would like to append this array to my XML file above.

memorys = []
for mem in wmiquery.Win32_PhysicalMemory():
    sysmem = {}
    sysmem['location'] = mem.DeviceLocator
    sysmem['banklabel'] = mem.BankLabel
    sysmem['cap'] = mem.Capacity
    memorys.append(sysmem)
for m in memorys:
    print(m)

The value of m is like this:

{'location': 'DIMM1', 'banklabel': 'ChannelA', 'cap': '8589934592'}
{'location': 'DIMM2', 'banklabel': 'ChannelA', 'cap': '8589934592'}

I would like to append these array to my XML. So my expectation based on the array above, I will append 2 new element. If the array has 4 then create new 4 element. Here is my expectation output:

<?xml version='1.0' encoding='utf-8'?>
<systemdata>
     <process>
          <number code="hsfg" class="hgdgf" tool="gagfa">
               <value type="string" />
               <value type="None" />
          </number>
          <!-- ID -->
          <id code="hsfg" class="gfdg" tool="fadg">
               <value type="string" />
               <value type="None" />
          </id>
     </process>
     <!-- memory -->
     <unitmemory>
          <!-- data -->
          <module location="DIMM1">
               <banklabel tool="banklabel">
                    <value type="string">ChannelA</value>
               </banklabel>
               <cap tool="cap">
                    <value type="string">8589934592</value>
               </cap>
          </module>             
          <module location="DIMM2">
               <banklabel tool="banklabel">
                    <value type="string">ChannelA</value>
               </banklabel>
               <cap tool="cap">
                    <value type="string">8589934592</value>
               </cap>
          </module>
     </unitmemory>
</systemdata>

Anyone can give me any idea?

2 Answers 2

1

Jack's approach seem easier, here is another way including the comments you need:

  1. read your file using a parser to keep comments
  2. insert comments using ET.Comment()
  3. loop through list of dictionaries and add sub-elements to xml
  4. use toprettyxml() to get convert xml to formatted string, but this adds unnecessary new-lines
  5. remove the extra newlines using list comprehension and strip()
  6. add encoding info to xml declaration
  7. write to original file
import xml.etree.ElementTree as ET
import xml.dom.minidom

memorys = [
    {'location': 'DIMM1', 'banklabel': 'ChannelA', 'cap': '100'},
    {'location': 'DIMM2', 'banklabel': 'ChannelB', 'cap': '200'}
]

m_encoding = 'utf-8'
parser = ET.XMLParser(target=ET.TreeBuilder(insert_comments=True))

tree = ET.parse('sampleXml.xml', parser=parser)
root = tree.getroot()

root.insert(1, ET.Comment('memory'))

unit_mem = ET.SubElement(root, 'unitmemory')
unit_mem.insert(0, ET.Comment('data'))

for mem in memorys:
    m_module = ET.SubElement(unit_mem, 'module ')
    m_module.set('location', mem['location'])

    b_label = ET.SubElement(m_module, 'banklabel  ')
    m_cap = ET.SubElement(m_module, 'cap ')
    b_value = ET.SubElement(b_label, 'value ')
    c_value = ET.SubElement(m_cap, 'value ')

    m_module.set('location', mem['location'])
    b_label.set('tool', 'banklabel')
    m_cap.set('tool', 'cap')
    b_value.set('type', 'string')
    c_value.set('type', 'string')

    b_value.text = mem['banklabel']
    c_value.text = mem['cap']

dom = xml.dom.minidom.parseString(ET.tostring(root))
xml_string = dom.toprettyxml()
xml_string = '\n'.join([line for line in xml_string.splitlines() if line.strip()])
part1, part2 = xml_string.split('?>')

with open("sampleXml.xml", 'w') as xfile:
    xfile.write(part1 + 'encoding=\"{}\"?>\n'.format(m_encoding) + part2)
    xfile.close()

My input

{'location': 'DIMM1', 'banklabel': 'ChannelA', 'cap': '100'}
{'location': 'DIMM2', 'banklabel': 'ChannelB', 'cap': '200'}

My output

<?xml version="1.0" encoding="utf-8"?>

<systemdata>
    <process>
        <number code="hsfg" class="hgdgf" tool="gagfa">
            <value type="string"/>
            <value type="None"/>
        </number>
        <!-- ID -->
        <id code="hsfg" class="gfdg" tool="fadg">
            <value type="string"/>
            <value type="None"/>
        </id>
    </process>
    <!--memory-->
    <unitmemory>
        <!--data-->
        <module location="DIMM1">
            <banklabel tool="banklabel">
                <value type="string">ChannelA</value>
            </banklabel>
            <cap tool="cap">
                <value type="string">100</value>
            </cap>
        </module>
        <module location="DIMM2">
            <banklabel tool="banklabel">
                <value type="string">ChannelB</value>
            </banklabel>
            <cap tool="cap">
                <value type="string">200</value>
            </cap>
        </module>
    </unitmemory>
</systemdata>
Sign up to request clarification or add additional context in comments.

Comments

1

There are a couple of ways to approach it, but I personally prefer this approach:

from lxml import etree

sd = """your xml above"""
memos = [[{'location': 'DIMM1', 'banklabel': 'ChannelA', 'cap': '8589934592'}],
[{'location': 'DIMM2', 'banklabel': 'ChannelB', 'cap': '123456'}]]
#I changed the second list a bit just to make the example clearer

doc = etree.XML(sd.encode())
destination = doc.xpath('//process')[0]

parent = """<unitmemory>"""

for mem in memos:
        attr_vals = [list(m.values()) for m in mem][0]
        new_child = f"""<module location="{attr_vals[0]}">
               <banklabel tool="banklabel">
                    <value type="string">{attr_vals[1]}</value>
               </banklabel>
               <cap tool="cap">
                    <value type="string">{attr_vals[2]}</value>
               </cap>
          </module>"""
        parent+=new_child

parent += """</unitmemory>"""
new_node = etree.fromstring(parent)
destination.addnext(new_node)
print(etree.tostring(doc).decode())

Output is your expected output.

1 Comment

hi @Jack Fleeting Thank you. But it return this error doc = etree.XML(sd.encode()) lxml.etree.XMLSyntaxError: Start tag expected, '<' not found, line 1, column 1

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.