Nested Loops for Hierarchical XML Data
The central pattern is findall() followed by a for loop: findall() collects matching Element objects, and the loop processes each Element individually.
Try it: XML Element Trees
How ET.fromstring() turns XML text into a tree of Elements (tag, attributes, .text), and how find(), findall() and iter() walk that tree along a path such as users/user.
How it works
- The parser reads start tags, text and end tags left to right, keeping a stack of open elements; each start tag becomes an Element that is a child of the element on top of the stack.
- Text before an element's first child becomes its .text (whitespace included); an end tag must match the open element or parsing stops with ParseError.
- findall(path) starts at the element it is called on (the root is not part of the path) and follows each path segment down one level of children.
- find(path) returns the first match or None; iter(tag) visits every element in document order, including the root.
- Reading .text, .tag or .get('attr') on each result gives the extracted values; reading an attribute from None raises AttributeError.
Default run (29 steps): ET.fromstring() receives 183 characters. The parser reads tags left to right and keeps a stack of open elements. … findall() returned a list of 2 elements; reading e.get('x') from each gives ['2', '7'].
Simplified: A small, hand-written XML parser for a subset: elements, attributes, text, self-closing tags, comments, CDATA, processing instructions and the five predefined entities plus numeric character references. DOCTYPE/DTDs and namespaces are not supported. Error messages follow Python's expat parser for these cases (column numbers can differ for non-ASCII documents). Paths support tags, *, ., //, [@attr], [@attr='v'], [tag] and [n].
Loading the simulation…
From One Match to Many
When XML contains a collection of records, retrieving only one element is not enough. A users list can contain many user records, a product catalog can contain many items, and a weather feed can contain forecasts for multiple days. The useful pattern is to select all matching elements, receive them as a Python list, and process the list one element at a time.
The central pattern is findall() followed by a for loop: findall() collects matching Element objects, and the loop processes each Element individually.
Reading the XML Hierarchy
Before writing the loop, picture the hierarchy. In the running structure, user elements are nested inside a users container. Each user contains id and name child elements and also has an x attribute. The path users/user means: move through the users element, then select the user elements inside it. The selected user elements are the records that the loop will process.
findall() returns a Python list of all Element objects matching a given path in the XML tree.
Tracing the Outer and Inner Steps
The outer step selects the collection of user elements. The for loop then advances through that collection one item at a time. During one iteration, the loop variable holds a complete Element object for one user. That object is not merely a text value: it represents the user subtree, so code inside the loop can call find() for a child element, get() for an attribute, or access .text for text content. After the work for one user finishes, the loop moves to the next user and repeats.
matches = root.findall("users/user")
for user in matches:
identifier = user.find("id").text
name = user.find("name").text
attribute_value = user.get("x")
print(identifier, name, attribute_value)- 1The path users/user follows the users container to its user children.
- 2The for loop runs once for each item in matches.
- 3Inside the loop, the current user Element is queried for id, name, and x.
- 4When the current iteration ends, the loop continues with the next matching Element.
findall()creates a list of matching user Element objects.- The loop variable user holds one Element object during each iteration.
find()locates a child element, and .text accesses that child's text content.get()accesses the x attribute on the current user element.- The print operation processes the current user's values before the loop advances.
Choosing find or findall
| Method | Result | Typical use in this pattern |
|---|---|---|
| find() | The first matching Element, or None if no match exists | Locate one child such as id or name inside the current user |
| findall() | A Python list containing all matching Element objects | Collect every user under the selected path before looping |
The important difference is whether the operation produces one matching element or a list of all matching elements.
Use find() when the operation is intended to retrieve one matching element, such as a child of the current user. Use findall() when the operation must collect multiple matches for later iteration. A common mistake is to treat the list returned by findall() as if it were a single Element. Instead, first loop through the list; then use find(), get(), or .text on the individual Element held by the loop variable.
Checking the Search Result
Always verify that the path matches the XML hierarchy. After calling findall(), check the length of the returned list with len(lst) to confirm that matches were found. If the list is empty, inspect the path and compare it with the actual nesting of the XML elements.
Common Looping Mistakes
Common mistake Using find() when every matching user is needed
What it looks likeuser = root.find("users/user")Why it failsfind()retrieves only the first matching element or None if no match exists.Do this insteadUse root.findall("users/user") to receive a list of all matching user elements, then iterate through that list.
Common mistake Trying to access child data on the whole list
What it looks likematches = root.findall("users/user") followed by matches.find("name")Why it failsfindall()returns a Python list, whilefind()is called on an individual Element.Do this insteadLoop through matches and call user.find("name") inside the loop.
Common mistake Using the wrong path for the hierarchy
What it looks likeroot.findall("user") when the user elements are nested inside users
Why it failsThe selected path must match the XML structure being searched.
Do this insteadPicture the hierarchy and use the path that travels through users to its user children.
Common mistake Forgetting that the loop variable is an Element object
What it looks likeTreating user as if it were already the name text
Why it failsEach loop item is a complete Element object that still needs a child lookup or attribute access.
Do this insteadUse user.find(), user.get(), or the relevant text access inside the loop.
Practice the Pattern
Assume root refers to an XML tree containing a users element with multiple user children. Write the two-stage pattern that collects every user and then processes each user in a for loop. Inside the loop, retrieve the id child text, the name child text, and the x attribute.
Hints
- Use the path users/user with
findall(). - The loop variable should represent one user Element.
- Use
find()followed by .text for id and name. - Use
get()for the x attribute.
Tracing a User Collection
Explain what happens when root.findall("users/user") is assigned to matches and a for loop iterates through matches.
- 1Select the path
The path users/user directs the search through the users container to the user elements inside it.
- 2Build the collection
findall()returns a Python list containing every matching user Element object. - 3Start the loop
The for loop takes the first Element from the list and assigns it to the loop variable.
- 4Read the current record
Inside the loop,
find()can locate id or name, .text can access child text content, andget()can access the x attribute. - 5Continue
After the current user is processed, the loop repeats for the next Element in the list.
The collection is retrieved once, and each user subtree is processed individually during the loop.
Key Takeaways
- 1
findall()returns a Python list of all Element objects matching a path. - 2A for loop processes the returned list one Element at a time.
- 3Each loop item is a complete Element object that can be queried with
find(),get(), and text access. - 4
find()retrieves one matching Element or None, whilefindall()retrieves all matching Elements in a list. - 5Check the path syntax and use
len()on the returned list when verifying that matches were found.
At a glance
- Use
findall()when an XML path may match multiple elements. - Iterate through the returned list with a for loop.
- Inside the loop, the current item is an Element object representing one XML subtree.
- Use
find()for a single child lookup,get()for an attribute, and text access for element content. - Verify the path and check the list length when no matches are found.