Skip to Content
Course content

99: Working with XML Data with ElementTree

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Look, I've seen this a dozen times: developers who are comfortable with JSON jump into XML and assume it behaves like a giant Python dictionary. They think they can just key into a tag to get its children. It's a logical leap, but in Python's ElementTree, it'll lead you straight into a wall.

XML Elements Aren't Python Dictionaries

Let's say we have a small XML snippet representing a retro game inventory. If you treat the root element like a dictionary, you're going to have a bad time.

import xml.etree.ElementTree as ET

xml_data = """<inventory>
    <game id="101">
        <title>Chrono Trigger></title>
        <platform>SNES></platform>
        <price>150.00></price>
    </game>
</inventory>"""

root = ET.fromstring(xml_data)

# The Misconception: Trying to access the 'game' tag like a key
try:
    game = root['game'] 
except TypeError as e:
    print(f"Error: {e}") 
# Output: Error: 'Element' object is not subscriptable

The error is clear: 'Element' object is not subscriptable. In ElementTree, the [] syntax is reserved strictly for attributes (the things inside the tag, like id="101"), not for child elements. If you try to access root['game'], Python thinks you're looking for an attribute named "game" on the root tag, and since it doesn't exist, it fails—or worse, if you used a different library, it might just return None. XML is a tree, not a hash map.

Navigating the Tree with find() and findall()

To actually get to your data, you need to use the methods provided by the Element class. I usually rely on find() when I know there's only one specific child I need, and findall() when I'm dealing with a list of similar items.

# To get the first 'game' element
game = root.find('game')

# To get the title (which is a child of game)
title_element = game.find('title')
print(title_element.text)  # Output: Chrono Trigger

# To get the attribute 'id' (this IS where the dictionary syntax works)
game_id = game.attrib['id'] 
print(f"Game ID: {game_id}") # Output: Game ID: 101

If you have a hundred games in that file, root.find('game') will only ever give you the first one. That's where findall() comes in. It returns a list of all matching elements, allowing you to loop through them. I highly recommend using a list comprehension here if you're just trying to extract a specific piece of data from every entry.

Modifying Data and Writing Back to Disk

Reading is the easy part. Changing the data is where things get slightly clunky. You don't "update" a tag so much as you modify the text property of the element object. Once you've made your changes, you have to tell the tree to write those changes back to a file.

# Let's raise the price of Chrono Trigger
game = root.find('game')
price = game.find('price')
price.text = "175.00"

# To save this back to a file, we need an ElementTree object
tree = ET.ElementTree(root)
tree.write("inventory.xml", encoding="utf-8", xml_declaration=True)

One quick tip: always include xml_declaration=True when writing. If you don't, you'll lose that <?xml version="1.0" ...?> header at the top, and some older XML parsers will absolutely throw a fit when they try to read your file later.




📋 Practical Task

Exercise: The Library Catalog Price Updater

You have been given an XML file named catalog.xml containing a list of books. Some books have their prices listed as "TBD" (To Be Determined). Your task is to write a script that parses this file, finds every book with a price of "TBD", and updates it to "19.99".

The XML Structure:

<catalog>
    <book category="fiction">
        <title>The Great Gatsby</title>
        <price>10.99</price>
    </book>
    <book category="non-fiction">
        <title>A Brief History of Time</title>
        <price>TBD</price>
    </book>
    <book category="fiction">
        <title>1984</title>
        <price>TBD</price>
    </book>
</catalog>

Requirements:

  • Load the catalog.xml file using ET.parse().
  • Iterate through all book elements.
  • Check if the price child element's text is exactly "TBD".
  • Update those specific prices to "19.99".
  • Save the changes back to a new file called updated_catalog.xml.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.