XML Tutorial · XML Basics · Lesson 2 of 28
XML Syntax
The XML declaration, the root element, start and end tags, empty elements, attributes, comments and processing instructions.
The parts of an XML document
<?xml version="1.0" encoding="UTF-8"?> <!-- 1. XML declaration (optional, must be first) -->
<!-- 2. A comment -->
<library> <!-- 3. The root element: exactly one -->
<book id="b1" lang="en"> <!-- 4. Start tag with attributes -->
<title>Dune</title> <!-- 5. Element with text content -->
<available/> <!-- 6. Empty element (self-closing) -->
</book> <!-- 7. End tag -->
<?print-style compact?> <!-- 8. Processing instruction -->
</library>
- The declaration states the XML version and character encoding. It's optional (UTF-8 is the default), but if present it must be the very first thing in the file, with no whitespace before it.
- There is exactly one root element; everything else lives inside it.
- Every start tag needs a matching end tag (
<title>…</title>), or the element is written self-closing (<available/>). - Attributes are
name="value"pairs in the start tag; values are always quoted (single or double quotes). - Comments are
<!-- … -->and can't contain--. - Processing instructions (
<?target data?>) pass hints to applications, like<?xml-stylesheet?>.
Names
Element and attribute names are case-sensitive (<Book> and <book> are different), must start with a letter
or underscore, and may contain letters, digits, -, _ and .. Names starting with xml (in any case) are
reserved.
The document is a tree
A parser turns the text into a tree of nodes. Walking it shows the structure:
from lxml import etree
doc = etree.fromstring(b"""<library>
<book id="b1" lang="en">
<title>Dune</title>
<available/>
</book>
<!-- more books soon -->
</library>""")
def show(node, depth=0):
pad = " " * depth
if isinstance(node, etree._Comment):
print(f"{pad}comment: {node.text.strip()}")
return
attrs = " ".join(f'{k}="{v}"' for k, v in node.attrib.items())
text = (node.text or "").strip()
print(f"{pad}<{node.tag}{' ' + attrs if attrs else ''}>{' text=' + repr(text) if text else ''}")
for child in node:
show(child, depth + 1)
show(doc)
print("root:", doc.tag, "| children:", len(doc))
Output
<library>
<book id="b1" lang="en">
<title> text='Dune'
<available>
comment: more books soon
root: library | children: 2Whitespace between elements is kept as text nodes. Most programs ignore it, but it matters for documents (mixed content) and when you compare or sign XML.
Test yourself
How many root elements can a well-formed XML document have?
Exactly oneOne or moreAny numberNone; the declaration is the root
Show answer
Answer: Exactly one
Every other element must be nested inside the single root (document) element.