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 levelSetWrap(int)— Sets column for automatic line wrappingSetFloatPrecision(int)— Controls decimal precision for floatsSetSeqFormat(EMITTER_MANIP)— Sets default sequence style (BlockorFlow)SetMapFormat(EMITTER_MANIP)— Sets default map style (BlockorFlow)
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:
include/yaml-cpp/emitter.h— Public API, operator overloads, and manipulator definitionssrc/emitter.cpp— Implements global setters and local manipulator handlerssrc/emitterstate.cpp— Stores formatting state and manages indentation stackssrc/indentation.h— Low-level functions that write spaces and newlines based on indent levelsinclude/yaml-cpp/emittermanip.h— Definitions ofEMITTER_MANIPvalues (e.g.,Block,Flow,Indent)
Summary
- Use
YAML::Emittermethods likeSetIndent()andSetWrap()for persistent, document-wide formatting changes - Apply local manipulators like
YAML::_Indentvia stream insertion for temporary, node-specific formatting - Local settings automatically reset after each node due to
StartedScalarclearingFmtScope::Localvalues - The
EmitterStateclass maintains formatting state usingm_groupsstack for nested structures - Refer to
emittermanip.hfor available manipulator constants andemitterstate.hfor 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →