4. Types and XML Representations

4.1. Representation summary

xmlio deliberately uses a small set of predictable XML shapes.

Built-in serialization forms

C++ value

XML shape

Notes

String, character, arithmetic value

One scalar element

Booleans are written as true or false.

std::complex<T>

<re> and <im> children

Both components use the scalar representation.

Arithmetic vector, list, or array

Whitespace-separated scalar text

Arithmetic vectors also accept <elem> input.

Structured vector, list, or array

Repeated <elem> children

Includes strings and user-defined records.

std::pair<A, B>

<First> and <Second> children

Each side may itself be structured.

std::map<K, V>

<elem><Key>...<Val>... records

Duplicate keys within one document are rejected.

std::optional<T>

The normal representation of T, or no element

An empty optional is omitted when writing.

Repeated scalar siblings

Several elements with the same name

Use read_many and write_many.

4.2. Scalar values

Floating-point output uses max_digits10 so values can round trip without precision loss. Scalar conversion is locale independent and rejects trailing characters. Boolean input accepts true, false, their common uppercase forms, and 1 or 0.

4.3. Complex numbers

<offset>
  <re>1.25</re>
  <im>-0.5</im>
</offset>
const auto offset =
    xmlio::read<std::complex<double>>(xml, "/settings/offset");

4.4. Sequential containers

Arithmetic containers are compact:

<weights>0.25 0.5 0.25</weights>

Strings and records use explicit elements:

<labels>
  <elem>small</elem>
  <elem>portable</elem>
</labels>

A fixed-size std::array additionally checks that the XML contains exactly the required number of values.

4.5. Pairs and maps

A pair is represented as:

<range>
  <First>0</First>
  <Second>10</Second>
</range>

A map is represented as an array of key-value records:

<catalog>
  <elem><Key>blue</Key><Val>1</Val></elem>
  <elem><Key>red</Key><Val>2</Val></elem>
</catalog>

Ordinary read replaces the destination map. Catalog workflows that combine several documents should use read_map_into and choose a collision policy:

DuplicateKeyPolicy::error

Reject a collision and leave the destination unchanged. This is the default.

DuplicateKeyPolicy::keep_existing

Keep the destination value and ignore an incoming value with the same key.

DuplicateKeyPolicy::overwrite

Replace the destination value with the incoming value.

Duplicate keys inside one incoming XML map are always errors, independent of the merge policy.

 1/**
 2 * @file catalog_merge.cpp
 3 * @brief Merge map-shaped XML catalogs with an explicit collision policy.
 4 */
 5
 6#include <xmlio/xmlio.hpp>
 7
 8#include <iostream>
 9#include <map>
10#include <string>
11
12int main() {
13  auto update = xmlio::XMLReader::from_string(R"xml(
14<root>
15  <catalog>
16    <elem><Key>blue</Key><Val>7</Val></elem>
17    <elem><Key>green</Key><Val>3</Val></elem>
18  </catalog>
19</root>
20)xml");
21
22  std::map<std::string, int> catalog{{"blue", 1}, {"red", 2}};
23  xmlio::read_map_into(update, "/root/catalog", catalog,
24                       xmlio::DuplicateKeyPolicy::overwrite);
25
26  for (const auto& [name, value] : catalog) {
27    std::cout << name << " = " << value << '\n';
28  }
29}

4.6. Optional values

Writing an empty std::optional omits the element. Reading into an optional resets it when the path does not exist:

std::optional<int> retries;
xmlio::read(xml, "/settings/retries", retries);

Use a required field with an explicit default in application code when absence has a documented meaning:

int retries = 3;
xmlio::read_optional(xml, "/settings/retries", retries);

4.7. Repeated siblings

write_many emits every range item under the same tag name. read_many reads scalar or string siblings selected by one XPath expression. Structured collections should normally use the standard <elem> container form so a single parent names and owns the collection.