Batu Lab NotesPractical developer guides

Distinguish missing XML text from an empty string

By Batu · English technical notes

Also published in our Blogger archive.

Distinguish missing XML text from an empty string

Element.text can be None, an empty string, or a non-empty string in an ElementTree object. Do not write if not element.text when the distinction matters: that condition groups None and "" together. First check whether find() returned an element, then compare its text value explicitly.

The example builds three child elements in memory. missing-text has the default None text, empty-text is explicitly assigned "", and message contains "enabled"; asking for an absent child forms a fourth case. text_state() returns a separate label for each state, and assertions establish the intended classification before it is printed.

There is an important parsing limitation. XML source such as <note></note> and <note/> contains no character data, and ElementTree normally represents both parsed elements with text is None. Consequently, ElementTree cannot recover an author’s lexical choice between those two empty XML spellings. An explicitly assigned empty string is useful for in-memory program state, but serialization may normalize it to an empty-element form. If the source-level spelling itself is meaningful, retain the original XML or use a parser and representation designed to preserve that lexical detail. These APIs are available in all currently supported Python versions.

AI assistance disclosure: this article was drafted with AI assistance and should be reviewed for the application’s null-versus-empty policy.

Source: Python ElementTree documentation.

import xml.etree.ElementTree as ET


def text_state(parent, tag):
    element = parent.find(tag)
    if element is None:
        return "element-missing"
    if element.text is None:
        return "text-missing"
    if element.text == "":
        return "text-empty"
    return f"text={element.text}"


settings = ET.Element("settings")
ET.SubElement(settings, "missing-text")
empty_text = ET.SubElement(settings, "empty-text")
empty_text.text = ""
ET.SubElement(settings, "message").text = "enabled"

assert text_state(settings, "missing-text") == "text-missing"
assert text_state(settings, "empty-text") == "text-empty"
assert text_state(settings, "message") == "text=enabled"
assert text_state(settings, "unknown") == "element-missing"

for name in ("missing-text", "empty-text", "message", "unknown"):
    print(f"{name}={text_state(settings, name)}")
missing-text=text-missing
empty-text=text-empty
message=text=enabled
unknown=element-missing