Customizing YAML Output Formatting and Indentation in yaml-cpp: A Complete Guide

Use the YAML::Emitter class with global Set* methods for persistent changes or stream manipulators like YAML::_Indent for temporary, node-specific formatting.

Customizing YAML output formatting and indentation in yaml-cpp gives you fine-grained control over how your documents are serialized. The library exposes this functionality through the YAML::Emitter class and its underlying EmitterState mechanism. By manipulating specific settings, you can adjust indentation width, line wrapping, scalar formatting, and collection styles.

How yaml-cpp Controls Output Formatting

The emitter's behavior is driven by a set of global and local manipulators of type EMITTER_MANIP. These values are stored in EmitterState (src/emitterstate.h), which tracks the current formatting configuration. When you call methods on YAML::Emitter, they modify this state to determine how subsequent nodes are serialized.

Global changes persist until explicitly modified, while local changes apply only to the immediate next node or block. This dual-scope system allows you to set baseline preferences while overriding them for specific structures.

Global vs Local Configuration

Global Format Manipulators

Global manipulators are applied through Emitter::Set* methods, which forward requests to EmitterState::Set* with a FmtScope::Global scope (see src/emitter.cpp, lines 73-95). These settings remain active until you change them again.

Common global methods include:

  • SetIndent(int) — Sets spaces per indentation level
  • SetWrap(int) — Sets column for automatic line wrapping
  • SetFloatPrecision(int) — Controls decimal precision for floats
  • SetSeqFormat(EMITTER_MANIP) — Sets default sequence style (Block or Flow)
  • SetMapFormat(EMITTER_MANIP) — Sets default map style (Block or Flow)

Local Format Manipulators

Local manipulators are injected via operator<< overloads defined in include/yaml-cpp/emitter.h. These call Emitter::SetLocalIndent (or similar methods), which records values with FmtScope::Local. According to the yaml-cpp source code in src/emitter.cpp (line 44), these only affect the following node or block.

When a node is emitted, EmitterState::StartedScalar clears any local modifications, ensuring subsequent nodes revert to the previous global configuration (see src/emitterstate.cpp, line 29).

Indentation Mechanics in Depth

The base indent (m_indent in EmitterState) determines how many spaces are added for each nesting level of block collections. When a new group starts, EmitterState::StartedGroup captures the current indent and pushes a Group object onto a stack (m_groups). The group's indent field initializes from GetIndent() (see src/emitterstate.cpp, line 56).

The actual space insertion happens through the SpaceOrIndentTo helper (declared in emitter.h, line 35). This function decides whether to emit a space or enough spaces to reach a target column, using the current column position and the desired indent level.

Available Formatting Options

The yaml-cpp emitter supports extensive customization through these configuration points:

Aspect Method Default
Character set SetOutputCharset EmitNonAscii
String quoting SetStringFormat Auto
Boolean style SetBoolFormat, SetBoolCaseFormat, SetBoolLengthFormat TrueFalseBool / LowerCase / LongBool
Null representation SetNullFormat TildeNull
Integer base SetIntBase Dec
Sequence format SetSeqFormat Block
Map format SetMapFormat Block
Indentation SetIndent (global) or operator<<(_Indent) (local) 2 spaces
Pre-comment indent SetPreCommentIndent 2
Post-comment indent SetPostCommentIndent 1
Wrapping column SetWrap 80
Float precision SetFloatPrecision max_digits10
Double precision SetDoublePrecision max_digits10
Trailing zeros SetShowTrailingZero false

Practical Code Examples

Setting Global Indentation and Line Wrapping

YAML::Emitter out;
out.SetIndent(4);          // use 4 spaces per level
out.SetWrap(120);          // wrap long lines at column 120

out << YAML::BeginMap;
out << YAML::Key << "title" << YAML::Value << "Demo";
out << YAML::Key << "list" << YAML::Value << YAML::BeginSeq;
out << "item1" << "item2" << "item3";
out << YAML::EndSeq << YAML::EndMap;

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

Applying Local Indentation for Specific Blocks

YAML::Emitter out;
out << YAML::BeginMap;
out << YAML::Key << "outer" << YAML::Value;

// Only this inner map uses an indent of 6 spaces
out << YAML::BeginMap << YAML::_Indent(6);
out << YAML::Key << "inner" << YAML::Value << "value";
out << YAML::EndMap;

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

Controlling Scalar Precision and Trailing Zeros

YAML::Emitter out;
out.SetFloatPrecision(5);           // 5 digits after decimal
out.SetShowTrailingZero(true);      // keep .0 on whole numbers

out << YAML::BeginMap;
out << "pi" << 3.14159;             // -> "3.14159"
out << "answer" << 42;              // -> "42.0"
out << YAML::EndMap;

Switching Between Block and Flow Styles

YAML::Emitter out;
out << YAML::BeginMap;
out << "compact" << YAML::BeginSeq << YAML::Flow << "a" << "b" << "c"
    << YAML::EndSeq;               // emits [a, b, c] on one line
out << YAML::EndMap;

Key Source Files Reference

Understanding these source files helps when customizing YAML output formatting and indentation in yaml-cpp:

Summary

  • Use YAML::Emitter methods like SetIndent() and SetWrap() for persistent, document-wide formatting changes
  • Apply local manipulators like YAML::_Indent via stream insertion for temporary, node-specific formatting
  • Local settings automatically reset after each node due to StartedScalar clearing FmtScope::Local values
  • The EmitterState class maintains formatting state using m_groups stack for nested structures
  • Refer to emittermanip.h for available manipulator constants and emitterstate.h for configuration storage

Frequently Asked Questions

How do I change the indentation level in yaml-cpp?

Call out.SetIndent(n) where n is the number of spaces per level. This sets the global indentation used for all subsequent block collections. Alternatively, use out << YAML::_Indent(n) for a local change that applies only to the next node.

What is the difference between global and local manipulators in yaml-cpp?

Global manipulators use FmtScope::Global and persist until explicitly changed. Local manipulators use FmtScope::Local and apply only to the immediate next node or block before reverting to the previous global setting. The StartedScalar method in EmitterState clears local modifications after each scalar emission.

How can I force flow style for a specific sequence or map?

Insert YAML::Flow into the stream before the sequence or map: out << YAML::BeginSeq << YAML::Flow << "a" << "b" << YAML::EndSeq. This outputs [a, b] on a single line instead of using block style with newlines.

Why are my local formatting changes not persisting across multiple nodes?

Local changes are designed to be temporary. According to the source code in src/emitterstate.cpp (line 29), the StartedScalar method clears any FmtScope::Local modifications after each node is emitted. Use global Set* methods if you need changes to persist across multiple nodes.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →