How to Iterate Over YAML Sequences and Maps in yaml-cpp: A Complete Guide
yaml-cpp provides a standard C++ iterator interface via YAML::Node::begin() and end() that yields YAML::Node references for sequences and std::pair-like iterator_value objects for maps, allowing range-based for loops and STL algorithms to traverse YAML documents naturally.
The yaml-cpp library parses YAML documents into a tree of YAML::Node objects. Whether you're processing configuration files or data pipelines, understanding how to traverse sequences (arrays) and maps (dictionaries) efficiently is essential for robust C++ applications. This guide explains the iterator architecture implemented in the jbeder/yaml-cpp repository and provides practical examples for iterating over YAML nodes.
Understanding the Iterator Architecture
According to the yaml-cpp source code, the iteration mechanism relies on a template-based foundation that abstracts the differences between sequence and map traversal. The public API exposes familiar begin() and end() methods on YAML::Node objects, but the underlying implementation distinguishes between node types through the detail::iterator_base<V> template.
In include/yaml-cpp/node/node.h, the YAML::Node class declares begin() and end() methods that return forward iterators. These methods instantiate iterator_base<detail::iterator_value>, defined in include/yaml-cpp/node/detail/iterator_fwd.h via the type aliases YAML::iterator and YAML::const_iterator.
The core implementation resides in include/yaml-cpp/node/detail/iterator.h. Here, the iterator_base template implements the forward-iterator requirements, while include/yaml-cpp/node/iterator.h defines detail::iterator_value. This struct inherits from Node but exposes first and second members (representing key and value) when iterating over maps.
How iterator_value Handles Node Types
When dereferencing an iterator, the operator* implementation (found in detail::iterator_base) constructs an iterator_value based on the underlying node structure:
- For sequences: The iterator yields a
Noderepresenting the element at that index. - For maps: The iterator yields an
iterator_valuewherefirstcontains the key node andsecondcontains the value node.
This design allows the same C++ iteration syntax to work seamlessly across both data structures.
Code Examples for yaml-cpp Iteration
Iterating Over a Sequence
When processing a YAML sequence (e.g., [1, 2, 3]), each dereferenced iterator returns a YAML::Node representing the scalar or complex element at that position:
YAML::Node root = YAML::Load("[1, 2, 3]");
for (const YAML::Node& elem : root) { // elem is a Node
std::cout << elem.as<int>() << '\n';
}
The elem variable is a Node object, allowing direct conversion to native C++ types via as<T>().
Iterating Over a Map
For YAML maps (e.g., {a: 10, b: 20}), the iterator returns a detail::iterator_value that exposes first (the key) and second (the value) as Node references:
YAML::Node root = YAML::Load("{a: 10, b: 20, c: 30}");
for (const YAML::Node& entry : root) { // entry is iterator_value
const YAML::Node& key = entry.first; // key node
const YAML::Node& value = entry.second; // value node
std::cout << key.Scalar() << ": " << value.as<int>() << '\n';
}
Because iterator_value inherits from Node, you can use entry directly where a Node is expected, but accessing first and second is required to separate keys from values.
Using STL Algorithms
The forward-iterator implementation in yaml-cpp supports standard C++ algorithms. You can pass map.begin() and map.end() to functions like std::accumulate:
YAML::Node map = YAML::Load("{x: 5, y: 7, z: 2}");
int sum = std::accumulate(map.begin(), map.end(), 0,
[](int acc, const YAML::Node& pair) {
return acc + pair.second.as<int>(); // pair.second is the value
});
std::cout << "Sum = " << sum << '\n';
This works because map.end() returns a valid sentinel iterator and dereferenced iterators expose the second member for value access.
Const vs. Mutable Iterators
The library provides both mutable and const iterators via iterator_fwd.h. Use const YAML::Node& for read-only access or YAML::Node& to modify values in place:
YAML::Node mutableRoot = YAML::Load("[10, 20]");
for (YAML::Node& n : mutableRoot) { // mutable reference
n = n.as<int>() * 2; // modify in-place
}
The begin() and end() methods return appropriate iterator types based on the const-qualification of the YAML::Node instance.
Key Source Files in yaml-cpp
Understanding the iterator implementation requires examining these specific files in the jbeder/yaml-cpp repository:
include/yaml-cpp/node/node.h: Declares theYAML::Nodeclass and the publicbegin()/end()API that returns forward iterators.include/yaml-cpp/node/detail/iterator_fwd.h: Defines theiteratorandconst_iteratortype aliases that map toiterator_base<detail::iterator_value>.include/yaml-cpp/node/detail/iterator.h: Contains the template implementation ofiterator_base<V>, which manages the underlyingnode_iteratorand constructsiterator_valueobjects during dereference.include/yaml-cpp/node/iterator.h: Definesdetail::iterator_value, the struct that represents both sequence elements and map entries (viafirst/secondmembers) while inheritingNodebehavior.
Summary
- yaml-cpp exposes standard forward iterators via
YAML::Node::begin()andend(), supporting range-based for loops and STL algorithms. - Sequence iteration yields
YAML::Nodeobjects directly, allowing immediate conversion to C++ types. - Map iteration yields
detail::iterator_valueobjects withfirst(key) andsecond(value) members to access paired data. - The implementation supports both const and mutable access patterns, enabling read-only traversal or in-place modification.
- Core iterator logic resides in
node.h,iterator_fwd.h, and thedetail/iterator.himplementation files.
Frequently Asked Questions
What is the difference between YAML::iterator and YAML::const_iterator?
YAML::iterator (defined in iterator_fwd.h) allows modification of the underlying YAML::Node elements during iteration, while YAML::const_iterator provides read-only access. The begin() and end() methods return the appropriate type based on the const-qualification of the node. For mutable nodes, use YAML::Node& elem in your loop; for const nodes, use const YAML::Node& elem.
How do I check if a YAML node is a sequence or map before iterating?
Use the IsSequence() or IsMap() methods on the YAML::Node object before entering the loop. For example:
if (node.IsSequence()) {
for (const auto& elem : node) { /* sequence logic */ }
} else if (node.IsMap()) {
for (const auto& pair : node) { /* map logic using pair.first/second */ }
}
This prevents runtime errors when accessing first or second on sequence nodes that don't expose those members.
Can I modify YAML values while iterating over a map?
Yes, if you use a non-const iterator. Declare your loop variable as YAML::Node& entry (instead of const YAML::Node& entry), then assign to entry.second to update the value. Note that you cannot modify the key (entry.first) during iteration, as this would violate the map's structural integrity. For sequences, you can modify the element directly via the loop reference.
What type does the iterator dereference operator return for sequences vs maps?
According to the implementation in detail/iterator.h, the dereference operator returns a detail::iterator_value in both cases. However, for sequences, this object behaves like a single YAML::Node representing the element. For maps, the same struct exposes first and second members containing the key and value nodes respectively. This unified interface allows the same iteration syntax to work for both node types while providing map-specific accessors when needed.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →