2

I have a situation where I want to add a particular element at the position and update if there is already present at the given position. Ex:

<items>
   <productid />
   <product_details>
       <entry>
           <key>?</key>
           <value>?</value>
       </entry>
   </product_details>
</items>

Now say for an ex: I pass the position as 1 and want to add values to the Key and Value, consider

parent = product_details
position = 1
key = ProductName
value = Maggi

Then it should update the xml since there is entry/key & entry/value at position 1 already. i.e.,

<items>
   <productid />
   <product_details>
       <entry>
           <key>ProductName</key>
           <value>Maggi</value>
       </entry>
   </product_details>
</items>

If I pass the position as 2, then it should be:

<items>
   <productid />
   <product_details>
       <entry>
           <key>?</key>
           <value>?</value>
       </entry>
       <entry>
           <key>ProductName</key>
           <value>Maggi</value>
       </entry>
   </product_details>
</items>

Edit: So far what I tried honestly is inserting at the position. Since I'm very new to Python, I'm bit confused on how to achieve this. The below code is slightly modified version of what I got from SO only. Ex:

  def to_xml(parent, xpath, value, index):
     nodes = parent.xpath(xpath)
     if nodes:
        node = nodes[0]
     else:
        parts = xpath.split('/')
        p = parent
        for part in parts:
           nodes = p.xpath(part)
           if not nodes:
              n = etree.XML("<%s/>" % part)
              p.append(n)
              p = n
           else:
              p = nodes[0]
        node = p

     node.text = str(value)
Vimalraj Selvam
  • 2,155
  • 3
  • 23
  • 52

3 Answers3

1

I've solved this problem myself. So this is what I'm doing, there may be a better version than this, please suggest:

  from lxml import etree

  def add_to_xml(tree, parent, subelement, index, column_value_split):
     element_found = tree.xpath("(.//" + parent + "/" + subelement + ")[" + index + "]")

     if not element_found:
        parent_element = tree.xpath(".//" + parent)[0]
        node_element = etree.XML("<" + subelement + "/>")
        parent_element.insert(int(index), node_element)

     for column_value in column_value_split:
        element, element_value = column_value.split("=")

        if element_found:
           item = element_found[0].xpath(element)[0]
           item.text = element_value
        else:
           sub_node_element = etree.XML("<" + element + "/>")
           node_element.append(sub_node_element)
           sub_node_element.text = element_value

  tree = etree.parse("products_list.xml")

  column_name = "product_details/entry/1"
  column_value = "key=ProductName;value=Maggi"

  parent, subelement, index = column_name.split("/")
  column_value_split = column_value.split(";")

  add_to_xml(tree, parent, subelement, index, column_value_split)
Vimalraj Selvam
  • 2,155
  • 3
  • 23
  • 52
0

Just in case you want another solution. I am assuming you are always modifying/adding entries. This solution will work with lxml and the batteries included xml.etree.ElementTree libraries.

#import xml.etree.ElementTree as etree
from lxml import etree

def upsert_entry(parent, index, key, value):
    entry_template = """
    <entry>
        <key>{0}</key>
        <value>{1}</value>
    </entry>
    """
    entries = parent.findall('./entry')
    # update if entry already exists.
    if index <= len(entries):
        entry = entries[index - 1]
        entry.find('key').text = key
        entry.find('value').text = value
    # insert at the end (only if the index is exactly after the last entry)
    elif index == len(entries) + 1:
        entry = etree.fromstring(entry_template.format(key, value))
        parent.append(entry)

tree = etree.parse('products_list.xml')
parent = tree.find('.//product_details')

upsert_entry(parent, 1, 'ProductName', 'Maggi')
upsert_entry(parent, 2, 'NewProductName', 'NewValue')
upsert_entry(parent, 5, 'NoAddedProductName', 'NoAddedValue')
dreyescat
  • 13,558
  • 5
  • 50
  • 38
0

For someone needing other option this work for me:

sig_root = etree.tostring(sig_root).replace('xxx</ds:DigestValue>',digest_keyinfo+'</ds:DigestValue>')
sig_root = etree.fromstring(sig_root)
J. Scott Elblein
  • 4,013
  • 15
  • 58
  • 94
PIIG
  • 1