# How to Parse YAML to C++ Objects Using the yaml-cpp Node API

> Learn to parse YAML to C++ objects with the yaml-cpp Node API. Load YAML documents and convert them to native types using powerful template helpers for efficient data handling.

- Repository: [Jesse Beder/yaml-cpp](https://github.com/jbeder/yaml-cpp)
- Tags: how-to-guide
- Published: 2026-07-11

---

**The yaml-cpp library represents YAML documents as a hierarchy of `YAML::Node` objects that you load from files or strings and convert to native C++ types using template helpers like `as<T>()` and `operator[]`.**

The **yaml-cpp** library (available at `jbeder/yaml-cpp`) provides a high-level **Node API** that transforms YAML parsing into a declarative process. Instead of writing event handlers, you load documents into a tree of `YAML::Node` instances and navigate them using container-like syntax. This approach lets you **parse YAML to C++ objects** with minimal boilerplate while maintaining type safety through template-based conversion specializations.

## Loading YAML Documents into Node Objects

The entry points for parsing are the free functions declared in [[`include/yaml-cpp/node/parse.h`](https://github.com/jbeder/yaml-cpp/blob/main/include/yaml-cpp/node/parse.h)](https://github.com/jbeder/yaml-cpp/blob/master/include/yaml-cpp/node/parse.h). These functions instantiate a `YAML::Parser` (defined in [[`include/yaml-cpp/parser.h`](https://github.com/jbeder/yaml-cpp/blob/main/include/yaml-cpp/parser.h)](https://github.com/jbeder/yaml-cpp/blob/master/include/yaml-cpp/parser.h)) to tokenize the input and build an in-memory node tree.

- **`YAML::LoadFile(const std::string& filename)`** reads a file from disk and returns the root node.
- **`YAML::Load(const std::string& input)`** parses a YAML string directly.
- **`YAML::LoadAll`** returns a `std::vector<YAML::Node>` for multi-document streams.

```cpp
#include <yaml-cpp/yaml.h>

// Parse a file
YAML::Node config = YAML::LoadFile("settings.yaml");

// Parse a string literal
YAML::Node data = YAML::Load("{host: localhost, port: 8080}");

```

Internally, these functions construct a `YAML::Parser` object that emits parsing events and constructs the node hierarchy stored in `detail::node` (see [[`include/yaml-cpp/node/impl.h`](https://github.com/jbeder/yaml-cpp/blob/main/include/yaml-cpp/node/impl.h)](https://github.com/jbeder/yaml-cpp/blob/master/include/yaml-cpp/node/impl.h)).

## Navigating the Node Hierarchy

The `YAML::Node` class (defined in [[`include/yaml-cpp/node/node.h`](https://github.com/jbeder/yaml-cpp/blob/main/include/yaml-cpp/node/node.h)](https://github.com/jbeder/yaml-cpp/blob/master/include/yaml-cpp/node/node.h)) behaves like a lightweight smart pointer to an internal representation. It provides familiar container semantics for traversing scalars, sequences, and maps.

### Scalar Access

Use **`Scalar()`** to retrieve the raw string representation, or **`as<T>()`** to convert to a specific C++ type:

```cpp
std::string name = config["user"].as<std::string>();
int id = config["id"].as<int>();

```

### Sequence Access

Access sequences by index using **`operator[]`** or iterate with range-based for loops:

```cpp
// Index-based access
int first = config["items"][0].as<int>();

// Iteration
for (const auto& element : config["items"]) {
    std::cout << element.as<std::string>() << '\n';
}

// Container operations
config["items"].push_back("new_item");
size_t count = config["items"].size();

```

### Map Access

Treat nodes as associative arrays using **`operator[]`** and check for key existence with **`contains()`**:

```cpp
std::string value = config["database"]["host"].as<std::string>();

// Insert or overwrite
config["database"]["ssl"] = true;

// Check existence before access
if (config["optional_key"].IsDefined()) {
    // ...
}

```

## Converting Nodes to C++ Types with `convert<T>`

The magic behind `Node::as<T>()` resides in the template specializations of `convert<T>` declared in [[`include/yaml-cpp/node/convert.h`](https://github.com/jbeder/yaml-cpp/blob/main/include/yaml-cpp/node/convert.h)](https://github.com/jbeder/yaml-cpp/blob/master/include/yaml-cpp/node/convert.h). These specializations handle standard library containers, arithmetic types, strings, booleans, and `std::pair`.

For example, converting a YAML sequence to `std::vector<int>` works because of this specialization:

```cpp
template <typename T, typename A>
struct convert<std::vector<T, A>> {
    static Node encode(const std::vector<T, A>& rhs) { /* ... */ }
    static bool decode(const Node& node, std::vector<T, A>& rhs) {
        if (!node.IsSequence()) return false;
        rhs.clear();
        for (const auto& element : node)
            rhs.push_back(element.as<T>());
        return true;
    }
};

```

This mechanism enables direct conversion to complex types:

```cpp
std::vector<int> primes = root["primes"].as<std::vector<int>>();
std::map<std::string, std::string> env = root["environment"].as<std::map<std::string, std::string>>();

```

## Handling Conversion Errors and Type Checking

Before converting, verify node types using **`IsScalar()`**, **`IsSequence()`**, or **`IsMap()`**. If `as<T>()` encounters a mismatch, it throws **`YAML::BadConversion`**. You can also use the fallback overload **`as<T>(default_value)`** to provide a default instead of catching exceptions.

```cpp
// Safe conversion with fallback
int timeout = config["timeout"].as<int>(30); // Uses 30 if key missing or not an int

// Explicit type checking
if (config["data"].IsSequence()) {
    auto list = config["data"].as<std::vector<std::string>>();
}

```

## Complete Example: Parsing a Configuration File

The following example demonstrates loading a YAML file, accessing nested structures, converting to standard containers, and emitting modifications back to YAML:

```cpp
#include <yaml-cpp/yaml.h>
#include <iostream>
#include <vector>
#include <map>

int main() {
    // 1. Load YAML from file
    YAML::Node config = YAML::LoadFile("example.yaml");

    // 2. Access scalar values
    std::string title = config["title"].as<std::string>();
    int version       = config["version"].as<int>();

    // 3. Convert sequences to std::vector
    std::vector<int> primes = config["primes"].as<std::vector<int>>();
    std::cout << "First prime: " << primes.front() << '\n';

    // 4. Convert maps to std::map
    std::map<std::string, std::string> settings =
        config["settings"].as<std::map<std::string, std::string>>();
    for (const auto& kv : settings)
        std::cout << kv.first << " = " << kv.second << '\n';

    // 5. Navigate nested structures
    double threshold = config["filters"]["threshold"].as<double>();
    std::cout << "Threshold: " << threshold << '\n';

    // 6. Safe access with fallback
    int retries = config["retries"].as<int>(3);

    // 7. Modify and emit back to YAML
    config["new_key"] = "added_value";
    std::cout << YAML::Dump(config) << '\n';
}

```

## Summary

- **Load documents** using `YAML::LoadFile` or `YAML::Load` from [`include/yaml-cpp/node/parse.h`](https://github.com/jbeder/yaml-cpp/blob/main/include/yaml-cpp/node/parse.h), which internally utilizes the `YAML::Parser` engine.
- **Navigate hierarchies** with `operator[]` for both sequences and maps, using `size()`, `push_back()`, and `contains()` for container operations.
- **Convert to C++ types** via `Node::as<T>()`, powered by `convert<T>` specializations in [`include/yaml-cpp/node/convert.h`](https://github.com/jbeder/yaml-cpp/blob/main/include/yaml-cpp/node/convert.h) that support standard containers and primitives.
- **Handle errors** by checking `IsScalar()`, `IsSequence()`, or `IsMap()` before conversion, or catch `YAML::BadConversion` exceptions; use `as<T>(fallback)` for safe defaults.

## Frequently Asked Questions

### What is the difference between `YAML::Load` and `YAML::LoadFile`?

`YAML::Load` accepts a `std::string` containing YAML text and parses it immediately into a `YAML::Node`, while `YAML::LoadFile` opens the specified file path and streams its contents through the parser. Both functions return a root node and use the same underlying parser implementation in [`include/yaml-cpp/parser.h`](https://github.com/jbeder/yaml-cpp/blob/main/include/yaml-cpp/parser.h), but `LoadFile` handles the file stream management automatically.

### How do I check if a key exists before accessing it?

Use the **`IsDefined()`** method or the **`contains(key)`** function on the parent node. `IsDefined()` returns false for uninitialized nodes (such as those returned by `operator[]` when the key is missing), while `contains()` explicitly checks for key existence in map nodes.

### Can I parse YAML into custom C++ structs?

Yes, by specializing **`YAML::convert<YourStruct>`** in the `YAML` namespace. You must implement a static `decode(const Node& node, YourStruct& rhs)` method that extracts values from the node and populates your struct, returning `true` on success. Once defined, you can call `root["key"].as<YourStruct>()` just like with built-in types.

### What exception does yaml-cpp throw for type mismatches?

The library throws **`YAML::BadConversion`** (derived from `YAML::Exception`) when `as<T>()` cannot convert the node's content to the requested type. The exception carries information about the line and column where the conversion failed, allowing you to diagnose schema errors in the input YAML.