Parsing XML to get all elem.tag: elem.text pairs I am parsing ICD-10 codes and have achieved a result that satisfies my base case, but I'm worried about how fragile my method is:

import xml.etree.ElementTree as ET

# A sample of the larger XML file I'm parsing
data = '''&lt;diag&gt;
&lt;name&gt;A00&lt;/name&gt;
&lt;desc&gt;Cholera&lt;/desc&gt;
&lt;diag&gt;
  &lt;name&gt;A00.0&lt;/name&gt;
  &lt;desc&gt;Cholera due to Vibrio cholerae 01, biovar cholerae&lt;/desc&gt;
  &lt;inclusionTerm&gt;
    &lt;note&gt;Classical cholera&lt;/note&gt;
  &lt;/inclusionTerm&gt;
&lt;/diag&gt;
&lt;diag&gt;
  &lt;name&gt;A00.1&lt;/name&gt;
  &lt;desc&gt;Cholera due to Vibrio cholerae 01, biovar eltor&lt;/desc&gt;
  &lt;inclusionTerm&gt;
    &lt;note&gt;Cholera eltor&lt;/note&gt;
  &lt;/inclusionTerm&gt;
&lt;/diag&gt;
&lt;diag&gt;
  &lt;name&gt;A00.9&lt;/name&gt;
  &lt;desc&gt;Cholera, unspecified&lt;/desc&gt;
&lt;/diag&gt;
&lt;/diag&gt;'''

# Create the tree
tree = ET.ElementTree(ET.fromstring(data))

# the `iter` method returns all tags in a given tree
# I'm just grouping the tags and texts here
def get_all_elements(tree):
    return [(elem.tag, elem.text) for elem in tree.iter()]

# This will return the desired elements from my tree
def parse_elements(tree):

    # First, get all of the elements in the tree
    elements = get_all_elements(tree)

    to_return = {}

    # This is what I think is too fragile. I'm basically looking
    # ahead for each element to see whether the next two elements
    # match the diag -&gt; name -&gt; desc sequence. But this indexing seems
    # to be too fragile at scale.
    for idx, elem in enumerate(elements):
        if 'diag' in elem and 'name' in elements[idx+1] and 'desc' in elements[idx+2]:
            name = elements[idx+1]
            desc = elements[idx+2]
            to_return[name[1]] = desc[1]
    return to_return

res = parse_elements(tree)


Let's see what's in res:

for k,v in res.items():
    print("name (code): ", k, "\n  desc: ", v)

name (code):  A00.9 
  desc:  Cholera, unspecified
name (code):  A00.0 
  desc:  Cholera due to Vibrio cholerae 01, biovar cholerae
name (code):  A00 
  desc:  Cholera
name (code):  A00.1 
  desc:  Cholera due to Vibrio cholerae 01, biovar eltor


So, I achieve my desired output, but I keep thinking there much be a better way to parse this XML. Unfortunately there's no &lt;diag&gt;&lt;subdiag&gt;&lt;/subdiag&gt;&lt;/diag&gt;-type of hierarchy. Even subdiagnoses are labeled with the &lt;diag&gt; tag. I suppose the above is my attempt at recursion -- although I have found true recursion tough with the current tag-naming. At the end of the day, I just need the name: desc pairs.

Edit:

There's also this approach:

names = [x.text for x in ET.fromstring(data).findall('.//name')]
descs = [x.text for x in ET.fromstring(data).findall('.//desc')]

res = zip(names, descs)


But I don't think this approach scales well, as the number of elements in names and descs differed by about 1000 when I tested this on the larger XML file. There were mismatches between codes and descriptions when I validated on actual codes.
XPath support fetching parent element https://docs.python.org/2/library/xml.etree.elementtree.html#supported-xpath-syntax. Let's try to find name element and get parent of this element:

&gt;&gt;&gt; [x for x in tree.findall('.//name/..')]
[&lt;Element 'diag' at 0x7f9c8cece278&gt;,
 &lt;Element 'diag' at 0x7f9c85a84908&gt;,
 &lt;Element 'diag' at 0x7f9c85a93f98&gt;,
 &lt;Element 'diag' at 0x7f9c85a8f188&gt;]


When we have parent element, we can get name and desc elements:

&gt;&gt;&gt; [(x.find('name'), x.find('desc')) for x in tree.findall('.//name/..')]
[(&lt;Element 'name' at 0x7f9c875ba7c8&gt;, &lt;Element 'desc' at 0x7f9c8cedfe58&gt;),
 (&lt;Element 'name' at 0x7f9c85a84958&gt;, &lt;Element 'desc' at 0x7f9c85a84f98&gt;),
 (&lt;Element 'name' at 0x7f9c85a8f048&gt;, &lt;Element 'desc' at 0x7f9c85a8f098&gt;),
 (&lt;Element 'name' at 0x7f9c85a8f1d8&gt;, &lt;Element 'desc' at 0x7f9c85a8f228&gt;)]


And finally:

&gt;&gt;&gt; [(x.find('name').text, x.find('desc').text) for x in tree.findall('.//name/..')]
[('A00', 'Cholera'),
 ('A00.0', 'Cholera due to Vibrio cholerae 01, biovar cholerae'), 
 ('A00.1', 'Cholera due to Vibrio cholerae 01, biovar eltor'), 
 ('A00.9', 'Cholera, unspecified')]