XML Tutorial · XML Basics · Lesson 3 of 28
Elements vs Attributes
When to store data in elements and when in attributes, how to design readable XML, and mixed content.
Two ways to say the same thing
<!-- attributes -->
<person id="7" name="Ada Lovelace" born="1815"/>
<!-- elements -->
<person>
<id>7</id>
<name>Ada Lovelace</name>
<born>1815</born>
</person>
Both are valid. Common guidelines:
| Use an attribute for | Use a child element for |
|---|---|
Identifiers and metadata (id, lang, currency, version) |
The actual data or content |
| Single, simple values | Values that can repeat (several phone numbers) |
| Values that never need structure | Values that may later need sub-elements or attributes |
| Short values | Long text, or text containing markup |
Attribute order is not significant, and an element can't have the same attribute twice. Element order is significant.
Mixed content
Documents (as opposed to data records) often mix text and elements:
<p>Water boils at <num unit="C">100</num> degrees at <em>sea level</em>.</p>
In tree APIs, text before the first child is the element's text, and text after a child is that child's
tail (lxml/ElementTree) or a separate text node (DOM). itertext() collects all of it:
from lxml import etree
p = etree.fromstring(b'<p>Water boils at <num unit="C">100</num> degrees at <em>sea level</em>.</p>')
print("text:", repr(p.text))
for child in p:
print(f"<{child.tag}> text={child.text!r} tail={child.tail!r}")
print("all text:", "".join(p.itertext()))
Output
text: 'Water boils at ' <num> text='100' tail=' degrees at ' <em> text='sea level' tail='.' all text: Water boils at 100 degrees at sea level.
Reading attributes and elements together
from lxml import etree
people = etree.fromstring(b"""<people>
<person id="7" born="1815"><name>Ada Lovelace</name><role>mathematician</role></person>
<person id="9" born="1906"><name>Grace Hopper</name><role>admiral</role></person>
</people>""")
for p in people:
print(p.get("id"), p.findtext("name"), p.get("born"), p.findtext("role"), sep=" | ")
Output
7 | Ada Lovelace | 1815 | mathematician 9 | Grace Hopper | 1906 | admiral
<?php
$people = simplexml_load_string('<people>
<person id="7" born="1815"><name>Ada Lovelace</name><role>mathematician</role></person>
<person id="9" born="1906"><name>Grace Hopper</name><role>admiral</role></person>
</people>');
foreach ($people->person as $p) {
echo implode(" | ", [$p['id'], $p->name, $p['born'], $p->role]), "\n";
}
Output
7 | Ada Lovelace | 1815 | mathematician 9 | Grace Hopper | 1906 | admiral
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.*;
import java.io.ByteArrayInputStream;
public class Main {
static String child(Element e, String name) {
return e.getElementsByTagName(name).item(0).getTextContent();
}
public static void main(String[] args) throws Exception {
String xml = """
<people>
<person id="7" born="1815"><name>Ada Lovelace</name><role>mathematician</role></person>
<person id="9" born="1906"><name>Grace Hopper</name><role>admiral</role></person>
</people>""";
NodeList people = DocumentBuilderFactory.newInstance().newDocumentBuilder()
.parse(new ByteArrayInputStream(xml.getBytes("UTF-8"))).getElementsByTagName("person");
for (int i = 0; i < people.getLength(); i++) {
Element p = (Element) people.item(i);
System.out.println(String.join(" | ", p.getAttribute("id"), child(p, "name"),
p.getAttribute("born"), child(p, "role")));
}
}
}
Output
7 | Ada Lovelace | 1815 | mathematician 9 | Grace Hopper | 1906 | admiral
Run this on your computer — see “Get Started” for setup.
Test yourself
Which is usually better stored as a child element rather than an attribute?
An idA list of phone numbersA language codeA version number
Show answer
Answer: A list of phone numbers
Attributes can't repeat on one element; repeating values belong in child elements.