s-blog

yaml-cpp 学习笔记

ssssmy · 2026-05-28 · 7 min · C++

yaml-cpp 学习笔记

yaml-cpp 是 C++ 下使用最广泛的 YAML 1.2 解析与生成库,纯 C++,无运行时依赖,CMake 友好。本笔记按「装 → 读 → 写 → 异常 → 集成」顺序梳理常用 API。

安装

方式一:源码编译 + 安装

git clone https://github.com/jbeder/yaml-cpp.git
cd yaml-cpp
cmake -S . -B build -DCMAKE_INSTALL_PREFIX=/path/to/install -DYAML_BUILD_SHARED_LIBS=ON
cmake --build build --config Release --target install

常用 CMake 选项:

选项 默认 说明
YAML_BUILD_SHARED_LIBS OFF 编译为动态库
YAML_CPP_BUILD_TESTS OFF 是否构建测试
YAML_CPP_BUILD_TOOLS OFF 是否构建命令行工具
YAML_CPP_INSTALL ON 是否生成 install 目标

方式二:vcpkg

vcpkg install yaml-cpp

方式三:Conan

conan install --requires=yaml-cpp/0.8.0

节点与类型

yaml-cpp 的核心类型是 YAML::Node,YAML 中的任何元素(标量 / 序列 / 映射)都用一个 Node 表示。

#include <yaml-cpp/yaml.h>

Node::Type() 返回值:

类型 含义
YAML::NodeType::Null null / 空节点
YAML::NodeType::Scalar 标量(字符串、数字、布尔)
YAML::NodeType::Sequence 序列(数组)
YAML::NodeType::Map 映射(键值对)
YAML::NodeType::Undefined 不存在的节点(访问越界)

判断方法:node.IsScalar() / IsSequence() / IsMap() / IsNull() / IsDefined()


加载 YAML

从文件加载

YAML::Node config = YAML::LoadFile("config.yaml");

从字符串加载

YAML::Node node = YAML::Load("name: yaml-cpp\nversion: 0.8.0");

多文档加载

YAML 支持用 --- 分割多文档,对应 LoadAllFromFile

std::vector<YAML::Node> docs = YAML::LoadAllFromFile("multi.yaml");
for (const auto& doc : docs) {
    // ...
}

读取与遍历

假设 config.yaml

name: yaml-cpp
version: 0.8.0
authors:
  - Jesse Beder
  - Contributors
options:
  shared: true
  tests: false

取字段

auto config  = YAML::LoadFile("config.yaml");
std::string name    = config["name"].as<std::string>();
std::string version = config["version"].as<std::string>();
bool shared         = config["options"]["shared"].as<bool>();

缺省值

如果字段可能不存在,用带参的 as<T>(default) 更安全:

bool tests = config["options"]["tests"].as<bool>(false);
int retries = config["retries"].as<int>(3);  // 字段不存在时返回 3

遍历 Sequence

for (const auto& author : config["authors"]) {
    std::cout << author.as<std::string>() << "\n";
}

遍历 Map

for (const auto& kv : config["options"]) {
    auto key = kv.first.as<std::string>();
    auto val = kv.second.as<bool>();
    std::cout << key << " = " << std::boolalpha << val << "\n";
}

类型转换

内置类型

int / double / bool / std::string 直接 as<T>() 即可。

STL 容器

std::vector / std::list / std::map 都支持开箱即用:

auto authors = config["authors"].as<std::vector<std::string>>();
auto options = config["options"].as<std::map<std::string, bool>>();

自定义类型

通过特化 YAML::convert<T> 给自己的类加上序列化能力。

struct Vec3 {
    double x, y, z;
};

namespace YAML {
template<>
struct convert<Vec3> {
    static Node encode(const Vec3& v) {
        Node node;
        node.push_back(v.x);
        node.push_back(v.y);
        node.push_back(v.z);
        return node;
    }

    static bool decode(const Node& node, Vec3& v) {
        if (!node.IsSequence() || node.size() != 3) return false;
        v.x = node[0].as<double>();
        v.y = node[1].as<double>();
        v.z = node[2].as<double>();
        return true;
    }
};
}

之后即可:

Vec3 pos = node["position"].as<Vec3>();

修改与写入

Node 可以直接像 STL 容器一样赋值:

YAML::Node node;
node["name"] = "demo";
node["count"] = 42;
node["tags"].push_back("alpha");
node["tags"].push_back("beta");

// 直接写到文件
std::ofstream fout("out.yaml");
fout << node;

输出:

name: demo
count: 42
tags:
  - alpha
  - beta

注意Node 是引用语义(指向同一份底层数据)。auto a = node["x"]; a = 1; 会改到 node["x"],不会创建副本。

Emitter — 精细控制输出格式

YAML::Emitter 可以控制风格(块式 / 流式 / 缩进 / 注释):

YAML::Emitter out;
out << YAML::BeginMap
    << YAML::Key << "name"   << YAML::Value << "yaml-cpp"
    << YAML::Key << "tags"   << YAML::Value
        << YAML::Flow << YAML::BeginSeq << "a" << "b" << YAML::EndSeq
    << YAML::Key << "config" << YAML::Value
        << YAML::BeginMap
            << YAML::Key << "shared" << YAML::Value << true
        << YAML::EndMap
    << YAML::EndMap;

std::cout << out.c_str();

输出:

name: yaml-cpp
tags: [a, b]
config:
  shared: true

常用 Manipulator:

Manipulator 作用
BeginMap / EndMap 开始/结束映射
BeginSeq / EndSeq 开始/结束序列
Key / Value 标记键值
Flow 用流式([a, b])输出
Block 用块式(默认)输出
Comment("...") 插入注释
Literal / DoubleQuoted 字符串风格

异常处理

yaml-cpp 抛 YAML::Exception 体系下的派生类,常见的有:

异常 触发场景
YAML::ParserException YAML 语法解析错误
YAML::BadFile 文件打开失败
YAML::BadConversion as<T>() 类型转换失败
YAML::KeyNotFound 强制访问不存在的键
YAML::InvalidNode 操作未初始化/无效节点

推荐做法:解析时抓 ParserException / BadFile,访问字段时用带默认值的 as<T>(default) 而非 try/catch 包每一处:

try {
    auto config = YAML::LoadFile("config.yaml");
    auto port = config["server"]["port"].as<int>(8080);
    // ...
} catch (const YAML::BadFile& e) {
    std::cerr << "config file missing: " << e.what() << "\n";
} catch (const YAML::ParserException& e) {
    std::cerr << "YAML syntax error: " << e.what() << "\n";
}

CMake 集成

安装到系统目录后,下游项目直接:

find_package(yaml-cpp REQUIRED)

add_executable(myapp main.cpp)
target_link_libraries(myapp PRIVATE yaml-cpp::yaml-cpp)

如果安装到了非默认路径,用 CMAKE_PREFIX_PATH 指向安装根目录:

cmake -S . -B build -DCMAKE_PREFIX_PATH=/path/to/install

或单独指定:

cmake -S . -B build -Dyaml-cpp_DIR=/path/to/install/lib/cmake/yaml-cpp

找不到 yaml-cpp-targets.cmake 的常见原因

  • 编译时未指定 -DYAML_CPP_INSTALL=ON(0.7+ 默认 ON,但旧版本需要显式打开)
  • 没跑 --target install,只编了库但没 install
  • CMAKE_PREFIX_PATH 没传到 cmake 命令
  • 同时装了 Debug / Release 但只 install 了其中一个 config

FetchContent 方式(不安装直接拉源码)

include(FetchContent)
FetchContent_Declare(
    yaml-cpp
    GIT_REPOSITORY https://github.com/jbeder/yaml-cpp.git
    GIT_TAG        0.8.0
)
set(YAML_CPP_BUILD_TESTS OFF CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(yaml-cpp)

target_link_libraries(myapp PRIVATE yaml-cpp::yaml-cpp)

参考

原文链接:https://www.ssssmy.com/notes/yaml-cpp-xue-xi-bi-ji