In Python ElementTree, How Do I Tell if an Element is a Comment?

Viewed 1019

I have an XML document that looks something like:

<!-- Servlet Context Listener -->
<listener>
<listener-class>
com.company.servlet.StartupShutdownListener
</listener-class>
</listener>
<!-- Servlet Class Definitions -->
<servlet>
<servlet-name>
AdminServlet
</servlet-name>
<servlet-class>
AdminServlet
</servlet-class>
<load-on-startup>
1
</load-on-startup>
</servlet>

To make it more human readable, I found the indent() function http://effbot.org/zone/element-lib.htm#prettyprint which makes output much nicer.

However, I want to further format only the Comment elements to make them easier to see. For example, simply putting an extra blank line before and after each comment makes the blocks easier to see for a human:

<!-- Servlet Context Listener -->

<listener>
  <listener-class>
    com.company.servlet.StartupShutdownListener
  </listener-class>
</listener>

<!-- Servlet Class Definitions -->

<servlet>
  <servlet-name>AdminServlet</servlet-name>
  <servlet-class>AdminServlet</servlet-class>
  <load-on-startup>1</load-on-startup>
</servlet>

How do I detect the Comment elements in my indent() function?

1 Answers

After fruitlessly searching the web for anyone else with this same issue, I turned to the source code (https://svn.python.org/projects/python/trunk/Lib/xml/etree/ElementTree.py). The answer is fairly simple:

import ElementTree as ET

...

def indent(elem, level=0):    # where elem is of type ET.Element
    ....
    if elem.tag is ET.Comment:
        ...

The key was realizing that while the "tag" attribute on a regular XML Element carries the XML tag name (e.g., "listener" or "servlet"), for an Element that represents an XML comment it is the Comment() function itself.

Here is the full updated indent() function to do the comment formatting as shown above:

def indent(elem, level=0, prev_elem=None, prev_level=0):        
    i = "\n" + level*"  "        
    if len(elem):        
        if not elem.text or not elem.text.strip():        
            elem.text = i + "  "        
        if not elem.tail or not elem.tail.strip():        
            elem.tail = i        
        prev_elem_local = elem        
        prev_level_local = level        
        for elem in elem:        
            indent(elem, level+1, prev_elem_local, prev_level_local)        
            prev_elem_local = elem        
            prev_level_local = level + 1        
        if not elem.tail or not elem.tail.strip():        
            elem.tail = i        
    else:        
        if level and (not elem.tail or not elem.tail.strip()):        
            elem.tail = i        
        if elem.tag is ET.Comment:        
            if prev_level == level:        
                prev_elem.tail = "\n" + prev_elem.tail        
            elif prev_level < level:        
                prev_elem.text = "\n" + prev_elem.text        
            elem.tail = "\n" + elem.tail        
Related