# Implementing Custom Type Conversion for User-Defined Types in yaml-cpp: A Complete Guide

> Master custom type conversion for user-defined types in yaml-cpp. Learn to specialize YAML::convert and integrate your C++ classes seamlessly with Node::as<T>() for efficient serialization.

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

---

**You enable seamless serialization of custom C++ classes in yaml-cpp by specializing the `YAML::convert<T>` template with static `encode()` and `decode()` methods, allowing your types to work natively with `Node::as<T>()` and the stream operator.**

The `yaml-cpp` library by jbeder represents every YAML document element as a `YAML::Node`, but converting between these nodes and your own C++ structs requires explicit type conversion logic. By implementing custom type conversion for user-defined types in yaml-cpp, you extend the library's template-based architecture to support domain-specific objects—from 3D vectors to configuration structs—without modifying the core parser.

## How yaml-cpp Handles Type Conversion

According to the yaml-cpp source code in [`include/yaml-cpp/node/convert.h`](https://github.com/jbeder/yaml-cpp/blob/main/include/yaml-cpp/node/convert.h), the library declares a primary template `YAML::convert<T>` that defines the interface for all type conversions. This header provides built-in specializations for primitives, STL containers, and strings (lines 79–511), while user-defined types require you to add your own specialization in the `YAML` namespace.

Each specialization must implement two static members:

- **`static Node encode(const T& rhs)`** – Constructs a `YAML::Node` from your C++ object.
- **`static bool decode(const Node& node, T& rhs)`** – Populates your C++ object from a `YAML::Node`, returning `true` on success.

The `decode` implementation for `bool` in [`src/convert.cpp`](https://github.com/jbeder/yaml-cpp/blob/main/src/convert.cpp) demonstrates the validation pattern used throughout the library, showing how scalar strings are mapped to Boolean values with appropriate error checking.

## Step-by-Step Implementation

### Defining Your Custom Type

First, create your user-defined type with any necessary comparison operators to facilitate testing.

```cpp
struct Vec3 {
    double x, y, z;
    bool operator==(const Vec3& other) const {
        return x == other.x && y == other.y && z == other.z;
    }
};

```

### Creating the YAML::convert Specialization

Place your specialization inside the `YAML` namespace to ensure argument-dependent lookup finds it. The specialization must live at global scope within this namespace.

```cpp
namespace YAML {
template<>
struct convert<Vec3> {
    // Encode: Convert Vec3 to YAML sequence [x, y, z]
    static Node encode(const Vec3& rhs) {
        Node node;
        node.push_back(rhs.x);
        node.push_back(rhs.y);
        node.push_back(rhs.z);
        return node;
    }

    // Decode: Extract from sequence, validating size and type
    static bool decode(const Node& node, Vec3& rhs) {
        if (!node.IsSequence() || node.size() != 3)
            return false;
        rhs.x = node[0].as<double>();
        rhs.y = node[1].as<double>();
        rhs.z = node[2].as<double>();
        return true;
    }
};
} // namespace YAML

```

### Encoding Maps Instead of Sequences

For objects that map naturally to YAML mappings, use `NodeType::Map` as demonstrated in the official [`docs/Tutorial.md`](https://github.com/jbeder/yaml-cpp/blob/main/docs/Tutorial.md).

```cpp
struct Person {
    std::string name;
    int age;
};

namespace YAML {
template<> struct convert<Person> {
    static Node encode(const Person& p) {
        Node n(NodeType::Map);
        n["name"] = p.name;
        n["age"] = p.age;
        return n;
    }
    
    static bool decode(const Node& n, Person& p) {
        if (!n.IsMap()) return false;
        p.name = n["name"].as<std::string>();
        p.age = n["age"].as<int>();
        return true;
    }
};
} // namespace YAML

```

## Using Custom Types with the Node API

When you assign a value to a node (`node["key"] = myObj;`), the compiler instantiates `YAML::convert<decltype(myObj)>` and calls the `encode` method. The `Node::as<T>()` method defined in [`src/node.cpp`](https://github.com/jbeder/yaml-cpp/blob/main/src/node.cpp) forwards to your `decode` implementation when extracting values.

```cpp
int main() {
    // Encoding
    Vec3 p{1.0, 2.0, 3.0};
    YAML::Node doc;
    doc["position"] = p;  // encode called here
    
    std::cout << doc << "\n";  // Emits: position: [1.0, 2.0, 3.0]
    
    // Decoding
    YAML::Node loaded = YAML::Load(doc);
    Vec3 q = loaded["position"].as<Vec3>();  // decode called here
    
    assert(p == q);
}

```

## Working with Collections of Custom Types

The conversion system composes automatically with STL containers. When storing a `std::vector<Person>`, yaml-cpp applies your `convert<Person>` specialization to each element recursively.

```cpp
std::vector<Person> team = { {"Alice", 30}, {"Bob", 25} };
YAML::Node root;
root["staff"] = team;  // encode called for each Person

std::cout << root << "\n";

```

## Summary

- **Specialize `YAML::convert<T>`** in the `YAML` namespace to register your type with the conversion system defined in [`include/yaml-cpp/node/convert.h`](https://github.com/jbeder/yaml-cpp/blob/main/include/yaml-cpp/node/convert.h).
- **Implement `encode()`** to serialize your object into a `YAML::Node` (scalar, sequence, or map).
- **Implement `decode()`** to validate the node structure (checking `IsSequence()`, `IsMap()`, or size) and populate your object, returning `false` on validation failure to trigger exceptions in `Node::as<T>()`.
- The mechanism integrates automatically with `YAML::Load` and the emitter (`operator<<`), requiring no changes to the core parser or [`src/convert.cpp`](https://github.com/jbeder/yaml-cpp/blob/main/src/convert.cpp).

## Frequently Asked Questions

### Why must the specialization reside in the YAML namespace?

The library locates conversion specializations through argument-dependent lookup. By placing your `template<> struct convert<YourType>` inside `namespace YAML`, you ensure the compiler finds your specialization when resolving `Node::as<YourType>()` or assignment operations that internally call `convert<T>::encode`.

### What happens if the decode function returns false?

When `decode` returns `false`, `Node::as<T>()` throws a `YAML::TypedBadConversion<T>` exception. You should validate node types (e.g., checking `IsSequence()` or `IsMap()`) and sizes before attempting extraction to ensure robust error handling, following the pattern used by `convert<bool>::decode` in [`src/convert.cpp`](https://github.com/jbeder/yaml-cpp/blob/main/src/convert.cpp).

### Can I use custom types inside standard containers?

Yes. The built-in specializations for `std::vector`, `std::map`, and other STL containers in [`include/yaml-cpp/node/convert.h`](https://github.com/jbeder/yaml-cpp/blob/main/include/yaml-cpp/node/convert.h) recursively call `convert<T>::encode` and `convert<T>::decode` for their element types. Once you define conversion for a custom type, it works automatically inside containers without additional code.

### Where is the primary convert<T> template defined?

The primary template declaration and all built-in specializations reside in [`include/yaml-cpp/node/convert.h`](https://github.com/jbeder/yaml-cpp/blob/main/include/yaml-cpp/node/convert.h) (lines 79–511), while [`src/convert.cpp`](https://github.com/jbeder/yaml-cpp/blob/main/src/convert.cpp) implements non-template helpers like the Boolean conversion logic. The [`docs/Tutorial.md`](https://github.com/jbeder/yaml-cpp/blob/main/docs/Tutorial.md) file provides the official reference example for implementing user-defined conversions.