How To Emit YAML

Jesse Beder edited this page Jan 10, 2016 · 2 revisions

Contents

Basic Emitting

The model for emitting YAML is std::ostream manipulators. A YAML::Emitter objects acts as an output stream, and its output can be retrieved through the c_str() function (as in std::string). For a simple example:

#include "yaml-cpp/yaml.h"

int main()
{
   YAML::Emitter out;
   out << "Hello, World!";

   std::cout << "Here's the output YAML:\n" << out.c_str(); // prints "Hello, World!"
   return 0;
}

Simple Lists and Maps

A YAML::Emitter object acts as a state machine, and we use manipulators to move it between states. Here's a simple sequence:

YAML::Emitter out;
out << YAML::BeginSeq;
out << "eggs";
out << "bread";
out << "milk";
out << YAML::EndSeq;

produces

- eggs
- bread
- milk

A simple map:

YAML::Emitter out;
out << YAML::BeginMap;
out << YAML::Key << "name";
out << YAML::Value << "Ryan Braun";
out << YAML::Key << "position";
out << YAML::Value << "LF";
out << YAML::EndMap;

produces

name: Ryan Braun
position: LF

These elements can, of course, be nested:

YAML::Emitter out;
out << YAML::BeginMap;
out << YAML::Key << "name";
out << YAML::Value << "Barack Obama";
out << YAML::Key << "children";
out << YAML::Value << YAML::BeginSeq << "Sasha" << "Malia" << YAML::EndSeq;
out << YAML::EndMap;

produces

name: Barack Obama
children:
  - Sasha
  - Malia

Using Manipulators

To deviate from standard formatting, you can use manipulators to modify the output format. For example,

YAML::Emitter out;
out << YAML::Literal << "A\n B\n  C";

produces

|
A
 B
  C

and

YAML::Emitter out;
out << YAML::Flow;
out << YAML::BeginSeq << 2 << 3 << 5 << 7 << 11 << YAML::EndSeq;

produces

[2, 3, 5, 7, 11]

Comments act like manipulators:

YAML::Emitter out;
out << YAML::BeginMap;
out << YAML::Key << "method";
out << YAML::Value << "least squares";
out << YAML::Comment("should we change this method?");
out << YAML::EndMap;

produces

method: least squares  # should we change this method?

And so do aliases/anchors:

YAML::Emitter out;
out << YAML::BeginSeq;
out << YAML::Anchor("fred");
out << YAML::BeginMap;
out << YAML::Key << "name" << YAML::Value << "Fred";
out << YAML::Key << "age" << YAML::Value << "42";
out << YAML::EndMap;
out << YAML::Alias("fred");
out << YAML::EndSeq;

produces

- &fred
  name: Fred
  age: 42
- *fred

STL Containers, and Other Overloads

We overload operator << for std::vector, std::list, and std::map, so you can write stuff like:

std::vector <int> squares;
squares.push_back(1);
squares.push_back(4);
squares.push_back(9);
squares.push_back(16);

std::map <std::string, int> ages;
ages["Daniel"] = 26;
ages["Jesse"] = 24;

YAML::Emitter out;
out << YAML::BeginSeq;
out << YAML::Flow << squares;
out << ages;
out << YAML::EndSeq;

to produce

- [1, 4, 9, 16]
-
  Daniel: 26
  Jesse: 24

Of course, you can overload operator << for your own types:

struct Vec3 { int x; int y; int z; };
YAML::Emitter& operator << (YAML::Emitter& out, const Vec3& v) {
    out << YAML::Flow;
    out << YAML::BeginSeq << v.x << v.y << v.z << YAML::EndSeq;
    return out;
}

and it'll play nicely with everything else.

Using Existing Nodes

We also overload operator << for YAML::Nodes in both APIs, so you can output existing Nodes. Of course, Nodes in the old API are read-only, so it's tricky to emit them if you want to modify them. So use the new API!

Output Encoding

The output is always UTF-8. By default, yaml-cpp will output as much as it can without escaping any characters. If you want to restrict the output to ASCII, use the manipulator YAML::EscapeNonAscii:

emitter.SetOutputCharset(YAML::EscapeNonAscii);

Lifetime of Manipulators

Manipulators affect the next output item in the stream. If that item is a BeginSeq or BeginMap, the manipulator lasts until the corresponding EndSeq or EndMap. (However, within that sequence or map, you can override the manipulator locally, etc.; in effect, there's a "manipulator stack" behind the scenes.)

If you want to permanently change a setting, there are global setters corresponding to each manipulator, e.g.:

YAML::Emitter out;
out.SetIndent(4);
out.SetMapStyle(YAML::Flow);

When Something Goes Wrong

If something goes wrong when you're emitting a document, it must be something like forgetting a YAML::EndSeq, or a misplaced YAML::Key. In this case, emitting silently fails (no more output is emitted) and an error flag is set. For example:

   YAML::Emitter out;
   assert(out.good());
   out << YAML::Key;
   assert(!out.good());
   std::cout << "Emitter error: " << out.GetLastError() << "\n";