2. Reading XML
2.1. Input sources
An XMLReader can load a file, XML text, an input stream, or the current
contents of an XMLBufferWriter:
auto file = xmlio::XMLReader::from_file("settings.xml");
auto text = xmlio::XMLReader::from_string("<settings><count>4</count></settings>");
std::istringstream stream("<settings><count>4</count></settings>");
xmlio::XMLReader streamed(stream);
xmlio::XMLBufferWriter buffer("settings");
xmlio::write(buffer, "count", 4);
xmlio::XMLReader buffered(buffer);
A default-constructed reader may be populated later with load_from_file,
load_from_string, or the corresponding open overload. Loading a new
source replaces the previous document.
2.2. Paths and scalar reads
Paths beginning with / are evaluated from the document root. Other paths
are evaluated from the reader’s current context:
const int count = xmlio::read<int>(xml, "/settings/count");
int another_count{};
xmlio::read(xml, "/settings/count", another_count);
A scalar read must select exactly one element, text node, or attribute. Element
text is only scalar when the selected element has no child elements. Numeric
conversion consumes the complete value, so 12 extra is not accepted as the
integer 12.
Strings preserve meaningful leading and trailing whitespace. Numeric parsing uses the classic locale and therefore expects a period as the decimal point.
2.3. Subreaders
A subreader shares its parent’s parsed document but changes the context used by relative paths:
xmlio::XMLReader settings(xml, "/document/settings");
const bool enabled = xmlio::read<bool>(settings, "enabled");
const int count = xmlio::read<int>(settings, "count");
Subreaders are movable, inexpensive, and side-effect free. An absolute path on
a subreader still addresses the original document root. The XPath expression
. selects the current context itself, which is occasionally useful inside
generic read functions.
2.4. Attributes
Attributes can be selected directly with XPath or read through the convenience functions:
const auto name = xmlio::read<std::string>(xml, "/project/@name");
const auto version = xmlio::read<int>(xml, "/project/@version");
std::string same_name;
xml.getAttribute("/project", "name", same_name);
2.5. Queries and optional values
exists reports whether an XPath expression has a result. count returns
the size of a selected node set:
if (xml.exists("/project/description")) {
// The field is present.
}
const int task_count = xml.count("/project/tasks/elem");
Use read_optional when absence is part of the schema:
const auto label =
xmlio::read_optional<std::string>(xml, "/project/label");
int retries = 3;
xmlio::read_optional(xml, "/project/retries", retries);
The second form leaves retries unchanged when the path is absent and
returns false. Conversion and query errors still propagate when a present
field is invalid.
2.6. Repeated siblings
read_many reads repeated scalar siblings selected by one XPath expression:
<tags>
<tag>small</tag>
<tag>portable</tag>
</tags>
std::vector<std::string> tags;
xmlio::read_many(xml, "/tags/tag", tags);
Containers represented by a parent with <elem> children use ordinary
read instead. Their exact representations are listed in
Types and XML Representations.
2.7. Reader diagnostics and mutation
str, root_str, context_str, and xpath_str return serialized
views of the document. Matching print functions write those views to a
stream. These operations are useful for diagnostics and extracting subtrees.
set and set_text replace the scalar content of exactly one selected
node. They are convenient for small transformations, but applications should
normally deserialize, validate, and rewrite structured data rather than use
the XML document as their primary mutable model.
2.8. Complete XPath example
The following example combines a predicate, an attribute, a subreader, a namespace, and an absent optional field:
1/**
2 * @file xpath_queries.cpp
3 * @brief Query attributes, predicates, namespaces, and optional values.
4 */
5
6#include <xmlio/xmlio.hpp>
7
8#include <iostream>
9#include <string>
10
11int main() {
12 const std::string document = R"xml(
13<inventory xmlns:m="urn:example:metrics">
14 <item id="A"><name>paper</name><stock>12</stock><m:score>8.5</m:score></item>
15 <item id="B"><name>pencil</name><m:score>9.0</m:score></item>
16</inventory>
17)xml";
18
19 auto inventory = xmlio::XMLReader::from_string(document);
20 inventory.registerNamespace("m", "urn:example:metrics");
21
22 xmlio::XMLReader pencil(inventory, "/inventory/item[@id='B']");
23 const auto name = xmlio::read<std::string>(pencil, "name");
24 const auto score = xmlio::read<double>(pencil, "m:score");
25 const auto stock = xmlio::read_optional<int>(pencil, "stock");
26
27 std::cout << inventory.count("/inventory/item") << " items; " << name
28 << " scores " << score << " and has stock data: "
29 << std::boolalpha << stock.has_value() << '\n';
30}