|
HowToEmitYAML
This outlines the basic methods for emitting a YAML document.
Featured Contents
Basic EmittingThe 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 MapsA 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 ManipulatorsTo 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 OverloadsWe 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 NodesWe 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 EncodingThe 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 ManipulatorsManipulators 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 WrongIf 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"; |
In the following example:
You need to remove the 1st "out << ages;" for it to make sense with the output.
Good catch!
How should one emit a UTF-8-coded text? I recieve only "\xffffffd0\ffffffa7..."
@fromagxo, it's currently not implemented. I'll get to it... (soon?)
Is there a way to write out a NULL value? For instance:
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.
Is there a way to update a node in an existing document? Or do you need to rewrite the whole document?
Currently, there's no way to modify an existing node. There may be one in the future, but I'm not sure.
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!
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?
Hi Micha,
Good idea! It's fixed (see issue 51 ).
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.
@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?
Thanks for the help. I did something similar to this to achieve what I wanted.
If i do :
It produces:
How can I prodeuce this:
Thanks
@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.
OK, thanks for the info.
I hope you do implement that, it would be awesome.
thanks
@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.
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
@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.
@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 ... ...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);@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.
@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
is skipped, and then in Sequence ParseBlock?() I have an exception at
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.
@Maestro,
I just confirmed that this is indeed a bug in 0.2.4, but it's been fixed in 0.2.5.
@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?
@Maestro, absolutely, and I'm subscribed to the RSS feed for the tag yaml-cpp.
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 .
@sergey
1. What exception? 2. Is ‘param‘ null? 3. Please read the front page - post questions on stackoverflow.com
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
YAML::Emitter emitter; emitter << YAML::BeginMap?; for(YAML::Iterator it=node.begin();it!=node.end();++it) { } emitter << YAML::EndMap?;How can I check if the current node is the "foo"?
Thanks in advance
Srimal.
@srimal, sorry, I forgot to update that with the removal of operator == on Nodes. Instead, use
if(it.first().to<std::string>() == "foo") { ... }I small typo in the example: out << YAML::BeginSeq? << v.x << v.y << v.z << YAML:EndSeq?; the last : should be ::
@tomvd, thanks, corrected!
How can I emit the following?
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?
@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.
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.
@philippe, yes that's fixed - it should even be in the 0.2.7 release.
It's not fixed in 0.2.7... see http://ideone.com/6Ok0L
@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!
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?().