My favorites | Sign in
Project Home Downloads Wiki Issues Source
Search
for
HowToEmitYAML  
This outlines the basic methods for emitting a YAML document.
Featured
Updated Sep 13, 2011 by jbe...@gmail.com

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.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, so you can output already-parsed nodes. Unfortunately, yaml-cpp does not yet provide a good way of modifying existing nodes, so currently the best way to do this is to use the Emitter and pick-and-choose from your node's children. For example:

YAML::Node node; // suppose this is a parsed map of scalars to scalars
                 // and we want to replace the value of the key "foo" with "bar"
YAML::Emitter emitter;
emitter << YAML::BeginMap;
for(YAML::Iterator it=node.begin();it!=node.end();++it) {
   emitter << YAML::Key << it.first();
   emitter << YAML::Value;
   if(it.first().to<std::string>() == "foo")
      emitter << "bar";
   else
      emitter << it.second();
}
emitter << YAML::EndMap;

It can get a bit unwieldy for more complex nodes, but it does the trick. Remember, though, that nodes retain no formatting (they represent data), so you may have to drill down to add what you want.

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";
Comment by philippe...@gmail.com, Jun 18, 2009

In the following example:

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;
out << ages;

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

You need to remove the 1st "out << ages;" for it to make sense with the output.

Comment by project member jbe...@gmail.com, Jun 18, 2009

Good catch!

Comment by fromagxo@gmail.com, Jul 23, 2009

How should one emit a UTF-8-coded text? I recieve only "\xffffffd0\ffffffa7..."

Comment by project member jbe...@gmail.com, Jul 23, 2009

@fromagxo, it's currently not implemented. I'll get to it... (soon?)

Comment by homechic...@gmail.com, Jul 29, 2009

Is there a way to write out a NULL value? For instance:

out << YAML::BeginMap;
out << YAML::Key << "version" << YAML::Value << 1;
out << YAML::Key << "optional" << YAML::Value << YAML::Null; // here
out << YAML::Key << "name" << YAML::Value << "my name";
Comment by project member jbe...@gmail.com, Jul 29, 2009

There is now :)

Your syntax should work fine now (it'll produce "~", the canonical null value). The reason I didn't previously implement it is that parsing empty doesn't work properly. I opened up  Issue 28 , if you're interested in following along.

Comment by deeznutz...@gmail.com, Oct 7, 2009

Is there a way to update a node in an existing document? Or do you need to rewrite the whole document?

Comment by project member jbe...@gmail.com, Oct 8, 2009

Currently, there's no way to modify an existing node. There may be one in the future, but I'm not sure.

Comment by micha.sp...@gmail.com, Oct 21, 2009

Hi, first of all, thanks for all your work, YAML is awesome!

I stumbled across a problem while trying to emit empty lists and maps, for example it should be possible to have something like this in a YAML document:

emptyList: []
emptyMap: {}

This way, you already know that it's a sequence or a map, but currently, it's empty. However, when I emit YAML::BeginSeq directly followed by YAML::EndSeq, it gives me the following exception: src/emitter.cpp:348: void YAML::Emitter::EmitEndSeq(): Assertion curState == ES_DONE_WITH_BLOCK_SEQ_ENTRY' failed. The same happens for empty maps. And I wouldn't like to add ~ as an entry to the list as a workaround.

Thanks!

Comment by micha.sp...@gmail.com, Oct 21, 2009

Oh, I already found what I was looking for: empty sequences or maps must be flowing, not blocky. But maybe you could automatically emit a flow-style sequence or map when it's empty?

Comment by project member jbe...@gmail.com, Oct 22, 2009

Hi Micha,

Good idea! It's fixed (see  issue 51 ).

Comment by kra...@gmail.com, Nov 22, 2009

Hello, I'm finding it very hard to modify YAML documents.

I would like to parse a document and then add or remove nodes to it. I don't mind if I have to create a new document that's based on an old one, but it doesn't seem like the library provides much functionality for composing documents together or modifying documents.

Any help or documentation on how this can currently be achieved? Ie. how to use the Emitter to emit two nodes?

Thanks.

Comment by project member jbe...@gmail.com, Nov 22, 2009

@kranar, there's unfortunately not a great way of doing this. I added a little explanation of how to use the current functionality. Does this help?

Comment by kra...@gmail.com, Nov 23, 2009

Thanks for the help. I did something similar to this to achieve what I wanted.

Comment by ze.p.sma...@gmail.com, Dec 2, 2009

If i do :

~YAML::Emitter out;
out << YAML::BeginSeq;
out << YAML::BeginMap;
out << YAML::Key << "name" << YAML::Value << "Fred";
out << YAML::Key << "age" << YAML::Value << "42";
out << YAML::EndMap;
out << YAML::BeginMap;
out << YAML::Key << "name" << YAML::Value << "John";
out << YAML::Key << "age" << YAML::Value << "43";
out << YAML::EndMap;
out << YAML::EndSeq;

It produces:

-
  name:Fred
  age:42
-
  name:John
  age:42

How can I prodeuce this:

- name:Fred
  age:42
- name:John
  age:42

Thanks

Comment by project member jbe...@gmail.com, Dec 2, 2009

@ze.p.smarques

Currently, there's no way. I've been thinking about how to effectively do that, and I haven't come up with a nice way. But it's going to happen somewhere down the road, I hope.

Comment by ze.p.sma...@gmail.com, Dec 2, 2009

OK, thanks for the info.

I hope you do implement that, it would be awesome.

thanks

Comment by project member jbe...@gmail.com, Dec 2, 2009

@ze.p.smarques, if you're interested in following along, it's  Issue 61 . I don't know when I'll get to it, though, but you can follow it and get an update when I do.

Comment by baeuml....@gmail.com, Dec 15, 2009

Hi,

is there a way to directly pass the output to an std::ostream (e.g. ofstream)?

e.g.

ofstream ofstr("output.yaml");
YAML::Emitter out(ofstr);
out << some_large_document;

// not necessary anymore:
// ofstr << out.c_str()

I guess it would speed up the process, especially on large files, if the output isn't fully cached in memory...

Thanks

Comment by project member jbe...@gmail.com, Dec 15, 2009

@baeuml.kit

Not yet, but there will be. I plan to refactor the emitter soon, and I'm going to add hooks to directly output to an std::ostream or a YAML::Node.

Comment by iwaitfor...@gmail.com, Jan 21, 2010

@ze.p.smarques

I faced the same issue; a simple patch (0.2.4 sources or SVN) did the job for me:

In emitter.cpp change the function to:

// EmitBeginMap
void Emitter::EmitBeginMap()
{
    if(!good())
        return;

    // must have a long key if we're emitting a map
    m_pState->StartLongKey();

    PreAtomicWrite();

    EMITTER_STATE curState = m_pState->GetCurState();
    EMITTER_MANIP flowType = m_pState->GetFlowType(GT_MAP);
    if(flowType == Block) {
        if(curState == ES_WRITING_BLOCK_SEQ_ENTRY || curState == ES_WRITING_BLOCK_MAP_KEY || curState == ES_WRITING_BLOCK_MAP_VALUE) {
            if(curState != ES_WRITING_BLOCK_SEQ_ENTRY) // <----- Added!! ----->
                m_stream << "\n";
            m_pState->UnsetSeparation();
        }
        m_pState->PushState(ES_WAITING_FOR_BLOCK_MAP_ENTRY);
    } else if(flowType == Flow) {
        EmitSeparationIfNecessary();
        m_stream << "{";
        m_pState->PushState(ES_WAITING_FOR_FLOW_MAP_ENTRY);
    } else
        assert(false);

    m_pState->BeginGroup(GT_MAP);
}

Works (for me) as supposed:

- filename: /export0/20091127.stream
  writer-class: stream::writer_simple
  reader-class: stream::reader_simple
  typefactory:
    - compound-name: order
      definition:
        - name: id
          type: uint64_t
        - name: security
          type: uint32_t
        - name: volume
          type: uint32_t
        - name: value
          type: double
...
...
Comment by Maestro....@gmail.com, Mar 16, 2010

Hi. I've got a situation here... Just trying to feed your aliases-examlples to parsing. And I have a YAML::ParserException?? at GetNextDocument??(doc). Emitter produces correct output and fails on it's way back. (The commented code with a NULL works fine).

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;
/*out << YAML::BeginSeq;
out << YAML::Anchor("fred") << YAML::Null;
out << YAML::Alias("fred");
out << YAML::EndSeq;*/
		

cout << "Here's the output YAML:\n" << out.c_str()<<endl;
std::stringstream stream(out.c_str());
YAML::Parser parser(stream);
YAML::Node doc;
parser.GetNextDocument(doc);
Comment by project member jbe...@gmail.com, Mar 16, 2010

@Maestro,

I can't reproduce your error. I copied and pasted your code exactly and it worked. What version are you using, what compiler, what OS, etc.?

By the way, in the future, please post questions on http://stackoverflow.com and tag them as yaml-cpp.

Comment by Maestro....@gmail.com, Mar 16, 2010

@jbeder, I use msvc2008. XP64 and XP32 at work. The error is the same. I tried msvc2005 too with a lib built from project files provided in your FAQ section. The result is the same. I doubt it's about system. I just did a run through debug, and I can say that it successfully detects sequence and founds anchor, saves anchor step

if(m_pContent)
m_pContent->Parse(pScanner, state);

is skipped, and then in Sequence ParseBlock?() I have an exception at

if(token.type != Token::BLOCK_ENTRY && token.type != Token::BLOCK_SEQ_END)
				throw ParserException(token.mark, ErrorMsg::END_OF_SEQ);

Any suggestions? What happened to MAP-part parsing? Oh, I use 2.4. Just noticed that 2.5 is already available. I'll try it tomorrow.

Comment by project member jbe...@gmail.com, Mar 16, 2010

@Maestro,

I just confirmed that this is indeed a bug in 0.2.4, but it's been fixed in 0.2.5.

Comment by Maestro....@gmail.com, Mar 17, 2010

@jbeder Yes, 0.2.5. passes the test. Thanks. Next time I'll need help I try that stackoverflow.com thing, do you check it on regular basis too?

Comment by project member jbe...@gmail.com, Mar 17, 2010

@Maestro, absolutely, and I'm subscribed to the RSS feed for the tag yaml-cpp.

Comment by sergey.a...@gmail.com, Mar 29, 2010

I was built your library on VS 2008 and included static library to my project. After starting application i got an exception when using

inline Emitter& operator << (Emitter& emitter, const char v) { return emitter.Write(std::string(v)); }

function

my method receive char* param .

YAML::Emitter emitter; 
emitter << YAML::BeginMap; 
emitter << YAML::Key << "param"; 
emitter << YAML::Value << param;
Comment by project member jbe...@gmail.com, Mar 30, 2010

@sergey

1. What exception? 2. Is ‘param‘ null? 3. Please read the front page - post questions on stackoverflow.com

Comment by srim...@gmail.com, Jul 25, 2011

Hi

It says

error: no match for ‘operator==’ in ‘it.YAML::Iterator::first() == "foo"’

when I run the code in http://code.google.com/p/yaml-cpp/wiki/HowToEmitYAML#Using_Existing_Nodes

YAML::Node node; // suppose this is a parsed map of scalars to scalars

// and we want to replace the value of the key "foo" with "bar"
YAML::Emitter emitter; emitter << YAML::BeginMap?; for(YAML::Iterator it=node.begin();it!=node.end();++it) {
emitter << YAML::Key << it.first(); emitter << YAML::Value; if(it.first() == "foo")
emitter << "bar";
else
emitter << it.second();
} emitter << YAML::EndMap?;

How can I check if the current node is the "foo"?

Thanks in advance

Srimal.

Comment by project member jbe...@gmail.com, Aug 23, 2011

@srimal, sorry, I forgot to update that with the removal of operator == on Nodes. Instead, use

if(it.first().to<std::string>() == "foo") { ... }
Comment by tomvd...@gmail.com, Sep 13, 2011

I small typo in the example: out << YAML::BeginSeq? << v.x << v.y << v.z << YAML:EndSeq?; the last : should be ::

Comment by project member jbe...@gmail.com, Sep 13, 2011

@tomvd, thanks, corrected!

Comment by philippe...@gmail.com, Nov 18, 2011

How can I emit the following?

# Food
- eggs
- cereals

# Drinks
- water
- coffee

I tried this:

YAML::Emitter out;
out << YAML::Comment("Food");
out << YAML::BeginSeq;
out << "eggs";
out << "cereals";
out << YAML::EndSeq;
out << YAML::Comment("Drinks");
out << YAML::BeginSeq;
out << "water";
out << "coffee";
out << YAML::EndSeq;

But that doesn't work. I found that creating multiple emmiters works tho:

std::string buffer;
{
YAML::Emitter out;
out << YAML::Comment("Food");
out << YAML::BeginSeq;
out << "eggs";
out << "cereals";
out << YAML::EndSeq;
buffer += out.c_str() + "\n";
}
{
YAML::Emitter out;
out << YAML::Comment("Drinks");
out << YAML::BeginSeq;
out << "water";
out << "coffee";
out << YAML::EndSeq;
buffer += out.c_str();
}

Is this really the only way?

Comment by project member jbe...@gmail.com, Nov 18, 2011

@philippe:

YAML::Emitter out;
out << YAML::Comment("Food");
out << YAML::BeginSeq;
out << "eggs" << "cereals";
out << YAML::Newline << YAML::Newline;
out << YAML::Comment("Drinks");
out << "water" << "coffee";
out << YAML::EndSeq;
std::cout << out.c_str() << "\n";

(Note you need two newlines because the first moves it to the next line, and the second creates the blank space.)

By the way, in the future, please ask questions like this on stackoverflow.com.

Comment by philippe...@gmail.com, Nov 19, 2011

Thanks! Btw, the bug with maps in a sequence that adds an additional newline is kinda annoying (see ze.p.sma...@gmail.com post from Dec 2, 2009). Is it fixed on trunk? Atm I fixed it by editing the source.

Comment by project member jbe...@gmail.com, Nov 19, 2011

@philippe, yes that's fixed - it should even be in the 0.2.7 release.

Comment by philippe...@gmail.com, Nov 21, 2011

It's not fixed in 0.2.7... see http://ideone.com/6Ok0L

Comment by project member jbe...@gmail.com, Nov 21, 2011

@philippe, interesting. I filed that as a new bug,  Issue 135 . You said that you fixed it by editing the source; how did you fix this one? (If you like, please comment on the issue itself.)

Thanks!

Comment by philippe...@gmail.com, Nov 21, 2011

I just commented the line inserting a new line in emmiter.cpp. This very likely broke something else, but for my use case "it works". I'm not sure what the right approach would be, but probably that you'd have to fiddle with m_pState->RequiresHardSeparation?().


Sign in to add a comment
Powered by Google Project Hosting