Edit Multiple XML Nodes in Multiple XML files

2.5k Views Asked by At

I have multiple XML files(60+) that I need to edit multiple text nodes(I think it's called). I am familiar with Java, JavaScript, Python, JQuery, PHP, HTML.

What language could I complete this with?

This is what I current have for a sample XML doc:

<?xml version="1.0" encoding="utf-8"?><bookstore>
    <book category="cooking">
        <title lang="en">Chinese</title>
        <author>chinese author</author>
        <year>2015</year>
        <price>fourth</price>
    </book>
    <book category="cooking">
        <title lang="en">All American</title>
        <author>American Author</author>
        <year>2015</year>
        <price>6.00</price>
    </book>
</bookstore>

So for example, I want to change the author and year of multiple elements at once!

This is my python code which will edit one node at a time. I need a loop or something to edit more at once.

from xml.dom.minidom import parse
import os

# create a backup of original file
new_file_name = 'dom.xml'
old_file_name = new_file_name + "~"
os.rename(new_file_name, old_file_name)

# change text value of element
doc = parse(old_file_name)
node = doc.getElementsByTagName('author')
node[0].firstChild.nodeValue = 'new author'


# persist changes to new file
xml_file = open(new_file_name, "w")
doc.writexml(xml_file, encoding="utf-8")
xml_file.close()

Any help would be greatly appreciated. Novice programmer here!

THANK YOU! :D

2

There are 2 best solutions below

1
On BEST ANSWER

Create functions:

def create_backup(new_file_name):
    """ create a backup of original file """
    old_file_name = new_file_name + "~"
    os.rename(new_file_name, old_file_name)
    return old_file_name

def change_author(doc, new_author)
    """ change text value of 'author' """
    node = doc.getElementsByTagName('author')
    node[0].firstChild.nodeValue = new_author

def save_changes(new_file_name, doc):
    """ persist changes to new file """
    xml_file = open(new_file_name, "w")
    doc.writexml(xml_file, encoding="utf-8")
    xml_file.close()

And now it's easy to create a loop:

file_names = ['dom.xml', ...]
for new_file_name in file_names:
    old_file_name = create_backup(new_file_name)
    doc = parse(old_file_name)
    change_author(doc, 'new author')
    save_changes(new_file_name, doc)
3
On

Personally, I'd do this with a shell script and XMLStarlet.

for f in *.xml; do
  xmlstarlet ed \
    -u '//author' -v 'new author' \
    <"$f" >"$f.new" && mv "$f.new" "$f"
done

If you only want to change the author for the book "All American", and to also change the price for that same book, this might instead be:

for f in *.xml; do
  xmlstarlet ed \
    -u '//book[title="All American"]/author' -v 'new author' \
    -u '//book[title="All American"]/price' -v 12.34 \
    <"$f" >"$f.new" && mv "$f.new" "$f"
done

Note that using hardcoded temporary file names is a bad practice if any other users can write to the directory you're using; should that be the case, using mktemp to generate a unique temporary file name would be more appropriate.