|
HowToParseADocument
This outlines the basic methods for parsing a YAML document.
Featured Contents
Basic ParsingThe parser accepts streams, not file names, so you need to first load the file. Since a YAML file can contain many documents, you can grab them one-by-one. A simple way to parse a YAML file might be: #include <fstream>
#include "yaml-cpp/yaml.h"
int main()
{
std::ifstream fin("test.yaml");
YAML::Parser parser(fin);
YAML::Node doc;
while(parser.GetNextDocument(doc)) {
// ...
}
return 0;
}Reading From the DocumentSuppose we have a document consisting only of a scalar. We can read that scalar like this: YAML::Node doc; // let's say we've already parsed this document std::string scalar; doc >> scalar; std::cout << "That scalar was: " << scalar << std::endl; How about sequences? Let's say our document now consists only of a sequences of scalars. We can use an iterator: YAML::Node doc; // already parsed
for(YAML::Iterator it=doc.begin();it!=doc.end();++it) {
std::string scalar;
*it >> scalar;
std::cout << "Found scalar: " << scalar << std::endl;
}... or we can just loop through: YAML::Node doc; // already parsed
for(unsigned i=0;i<doc.size();i++) {
std::string scalar;
doc[i] >> scalar;
std::cout << "Found scalar: " << scalar << std::endl;
}And finally maps. For now, let's say our document is a map with all keys/values being scalars. Again, we can iterate: YAML::Node doc; // already parsed
for(YAML::Iterator it=doc.begin();it!=doc.end();++it) {
std::string key, value;
it.first() >> key;
it.second() >> value;
std::cout << "Key: " << key << ", value: " << value << std::endl;
}Note that dereferencing a map iterator is undefined; instead, use the first and second methods to get the key and value nodes, respectively. Alternatively, we can pick off the values one-by-one, if we know the keys: YAML::Node doc; // already parsed std::string name; doc["name"] >> name; int age; doc["age"] >> age; std::cout << "Found entry with name '" << name << "' and age '" << age << "'\n"; One thing to be keep in mind: reading a map by key (as immediately above) requires looping through all entries until we find the right key, which is an O(n) operation. So if you're reading the entire map this way, it'll be O(n^2). For small n, this isn't a big deal, but I wouldn't recommend reading maps with a very large number of entries (>100, say) this way. Optional KeysIf you try to access a key that doesn't exist, yaml-cpp throws an exception (see HowToParseADocument#When_Something_Goes_Wrong When Something Goes Wrong). If you have optional keys, you it's often easier to use FindValue instead of operator[]: YAML::Node doc; // already parsed
if(const YAML::Node *pName = doc.FindValue("name")) {
std::string name;
*pName >> name;
std::cout << "Key 'name' exists, with value '" << name << "'\n";
} else {
std::cout << "Key 'name' doesn't exist\n";
}Getting More ComplicatedThe above three methods can be combined to read from an arbitrary document. But we can make life a lot easier. Suppose we're reading 3-vectors (i.e., vectors with three components), so we've got a structure looking like this: struct Vec3 {
float x, y, z;
};We can read this in one operation by overloading the extraction (>>) operator: void operator >> (const YAML::Node& node, Vec3& v)
{
node[0] >> v.x;
node[1] >> v.y;
node[2] >> v.z;
}
// now it's a piece of cake to read it
YAML::Node doc; // already parsed
Vec3 v;
doc >> v;
std::cout << "Here's the vector: (" << v.x << ", " << v.y << ", " << v.z << ")\n";A Complete ExampleHere's a complete example of how to parse a complex YAML file: monsters.yaml - name: Ogre
position: [0, 5, 0]
powers:
- name: Club
damage: 10
- name: Fist
damage: 8
- name: Dragon
position: [1, 0, 10]
powers:
- name: Fire Breath
damage: 25
- name: Claws
damage: 15
- name: Wizard
position: [5, -3, 0]
powers:
- name: Acid Rain
damage: 50
- name: Staff
damage: 3main.cpp #include "yaml-cpp/yaml.h"
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
// our data types
struct Vec3 {
float x, y, z;
};
struct Power {
std::string name;
int damage;
};
struct Monster {
std::string name;
Vec3 position;
std::vector <Power> powers;
};
// now the extraction operators for these types
void operator >> (const YAML::Node& node, Vec3& v) {
node[0] >> v.x;
node[1] >> v.y;
node[2] >> v.z;
}
void operator >> (const YAML::Node& node, Power& power) {
node["name"] >> power.name;
node["damage"] >> power.damage;
}
void operator >> (const YAML::Node& node, Monster& monster) {
node["name"] >> monster.name;
node["position"] >> monster.position;
const YAML::Node& powers = node["powers"];
for(unsigned i=0;i<powers.size();i++) {
Power power;
powers[i] >> power;
monster.powers.push_back(power);
}
}
int main()
{
std::ifstream fin("monsters.yaml");
YAML::Parser parser(fin);
YAML::Node doc;
parser.GetNextDocument(doc);
for(unsigned i=0;i<doc.size();i++) {
Monster monster;
doc[i] >> monster;
std::cout << monster.name << "\n";
}
return 0;
}When Something Goes Wrong... we throw an exception (all exceptions are derived from YAML::Exception). If there's a parsing exception (i.e., a malformed YAML document), we throw a YAML::ParserException: try {
std::ifstream fin("test.yaml");
YAML::Parser parser(fin);
YAML::Node doc;
parser.GetNextDocument(doc);
// do stuff
} catch(YAML::ParserException& e) {
std::cout << e.what() << "\n";
}If you make a programming error (say, trying to read a scalar from a sequence node, or grabbing a key that doesn't exist), we throw some kind of YAML::RepresentationException. To prevent this, you can check what kind of node something is: YAML::Node node;
YAML::NodeType::value type = node.Type(); // should be:
// YAML::NodeType::Null
// YAML::NodeType::Scalar
// YAML::NodeType::Sequence
// YAML::NodeType::MapNote about copying YAML::NodeCurrently YAML::Node is non-copyable, so you need to do something like const YAML::Node& node = doc["whatever"]; This is intended behavior. If you want to copy a node, use the Clone function: std::auto_ptr<YAML::Node> pCopy = myOtherNode.Clone(); The intent is that if you'd like to keep a YAML::Node around for longer than the document will stay in scope, you can clone it and store it as long as you like. |
You reference test.yaml in the "HowToParseADocument" page. It would be nice for people new to yaml to have a copy of text.yaml either pasted on that page or a link to it so we can see the contents & how it's being parsed. It's easier to understand that way.
Thanks. Steve
Yeah, maybe a large file with a lot o "Monsters" so we can have a look at the structure of the file being parsed. Anyway, great library!
Lucas
how can you parse map of maps:
a: { a: 5, b: 6, c: 7 }
@prog112 Just parse one, then the other. E.g.,
node["a"]["b"] >> x;
should set x to 6
@Steve and Lucas - good idea! I added a complete example.
Could you please also provide help with compiling source on windows? On linux everything was fine, but here...
CMake Error: CMake was unable to find a build program corresponding to "Visual S tudio 6". CMAKE_MAKE_PROGRAM is not set. You probably need to select a differe nt build tool. CMake Error: Could not find cmake module file:C:/Users/prog/CMakeFiles/CMakeCCom piler.cmake CMake Error: Could not find cmake module file:C:/Users/prog/CMakeFiles/CMakeCXXC ompiler.cmake
Do I really need M$ compiler? Can't use gcc? Sorry, I'm new to programming and not really familiar with CMake tool.
Hi,
I have one question about the parser, is your parser able to parse any hexadecimal, date/timestamp, etc to the corresponding type of value. let say I have the following yaml: hexadecimal: 0x1234 Can it be read like: int hex; doc["hexadecimal"] >> hex;
Do you handle all the types defined here: http://yaml.org/type/index.html ?
thanks! I am developping on windows, until now using xml for data processing, I am trying yaml. I think it will be much faster than MSXML...
Hi again,
I have another question, let's say I have optional nodes, that may OR NOT appear in my yaml file. What I don't really like, is that to do this I have to do a try/catch(RepresentationException??) because the value may not exist, for each value. what do you think?!
here is expl: int value1, value2; try {
} catch (YAML::RepresentationException?& e) {} try { } catch (YAML::RepresentationException?& e) {}thanks
Hi again,
sorry for these lot of questions, but as you don't have any tags in svn, should we guess that what is in the trunk is something stable?
@bobbycoul
Right now we only parse integers though std::stringstream, so it won't properly read 0x1234. This will be done eventually, I suppose. As for the other data types, the only implemented special case is for booleans.
For optional nodes, I just patched it to add Node::FindValue?, so you can do:
if(const Node *pValue = node.FindValue("param1")) { int value1; *pValue >> value1; }or something like that.
Finally, as for a stable branch, I haven't really thought about tagging one as such. The most recent version is almost always the most stable. (I personally always use the latest branch version in my other projects, but I suppose that's "dogfooding".)
Hi,
Thanks for the reply. Now I have some things to tell/ask you:
-is it possible for you to maintain a VS2005 project/solution files, because I don't use VS2008, VS2005 cannot open the files, I recreated them anyway. I can give you the ones I created if you provide me your email address.
The following applies to revision 200. In your recent modifications I get the following errors:
>Cannot open source file: '.\src\alias.cpp': No such file or directory I have seen that you renamed the file to aliascontent.cpp/.h
>src\aliascontent.cpp(11) : warning C4138: '/' found outside of comment You should separate the and /.../
>src\node.cpp(22) : warning C4355: 'this' : used in base member initializer list according to msdn, seems dangerous to do this
>src\sequence.cpp(48) : warning C4267: 'return' : conversion from 'size_t' to 'unsigned int', possible loss of data I get many of these warnings, it's not error, but I appreciate having no warnings...
>src\stream.cpp(411) : warning C4996: 'std::basic_streambuf<Elem,Traits>::sgetn': Function call with parameters that may be unsafe - this call relies on the caller to check that the passed values are correct. To disable this warning, use -D_SCL_SECURE_NO_WARNINGS. See documentation on how to use Visual C++ 'Checked Iterators'
well, that's all ahah. Anyway this project is nice. continue like this!
I have seen you have added utf stuff, does it mean I can create a yaml file encoded in UTF8 with(out) bom?
regards.
In general, I'd rather not have extra project/solution files. I'm already a little hesitant to have VS2008 project files, because you can autogenerate them from CMake (which is the point of having a CMake file).
Specifically:
1. The warning about */ outside a comment is maybe a silly warning, but I suppose I may fix that 2. As for using thisin an initialization list, it is (like all things) dangerous if used improperly, but I'm just storing the pointer, not doing anything with it. 3. The int/unsigned/size_t business is annoying, and I probably should use size_t everywhere, but it's no fun to type :) 4. Visual Studio complains a lot about unsafe function calls, and sometimes it's OK to use them :)
And as for the latest UTF stuff, you can do anything the YAML spec allows. So yes, if it's UTF8, you don't need a BOM.
mmm thanks for the reply. I can read UTF8 encoded file, but as it's read in a "std::string", it doesn't really make sense, I think it should be converted to wstring?!
Also I would like to know if I can parse this kind of yaml file with your yaml-cpp code:
--- version: 1.0 date: 2009-07-15 list: - id: 1 name: name1 sublist: - name: list1name list: [word1, word2, word3] - id: 2 name: name2 sublist: - name: list2name list: [word4, word5, word6]or are there limitations with your implementation right now?
regards & thanks.
The latest commit actually implemented the conversion to wstring. So you can directly read a wstring from a YAML::Node.
And yes, the parser is (to my knowledge) complete.
Hi, thanks for the fast reply!
about wstring, well you convert string to wstring so unicode strings are not correctly read from the yaml file, let's say you have this:
you see what i mean? what could be nice to do to get it would be something like:
with the cpp file encoded with unicode or utf8.
do you get my point? So in this case all could be in utf8, the keys, and also any text in the file. I don't want to bother you of course, but just to make yaml-cpp better! And as you are very active in your project, I take the opportunity. Anyway I think these kind of modifications are benefit for everybody, and I am not the first one to ask for utf8 support.
regards & thanks!
Good point. I opened up an issue with this ( Issue 22 ) - I'm still new to different character sets, so I'm not sure how to fix this.
P.S. You can direct all further comments about this to the issue, and I'll respond there. If you have any other questions, you can either open up an issue (if it's a bug or something that needs clarification for others), or send me an email (my address is my user name @gmail.com)
Hello, just wanted to contribute a recursive depth first function, for a yaml-cpp YAML::Node document:
void traverse(const YAML::Node & node, unsigned int depth = 0) { // recursive depth first YAML::CONTENT_TYPE type = node.GetType(); string indent((size_t)depth, '\t'); string out; switch (type) { case YAML::CT_SCALAR: node >> out; cout << indent << "SCALAR: " << out << endl; break; case YAML::CT_SEQUENCE: cout << indent << "SEQUENCE:" << endl; for (unsigned int i = 0; i < node.size(); i++) { const YAML::Node & subnode = node[i]; cout << indent << "[" << i << "]:" << endl; traverse(subnode, depth + 1); } break; case YAML::CT_MAP: cout << indent << "MAP:" << endl; for (YAML::Iterator i = node.begin(); i != node.end(); ++i) { const YAML::Node & key = i.first(); const YAML::Node & value = i.second(); key >> out; cout << indent << "KEY: " << out << endl; cout << indent << "VALUE:" << endl; traverse(value, depth + 1); } break; case YAML::CT_NONE: cout << indent << "(empty)" << endl; break; default: cerr << "Warning: traverse: unknown/unsupported node type" << endl; } } // traverseTested with g++ (GCC) 3.2.2.
Thanks, Jørn.
Thanks , Jørn! However:
1. This doesn't appear to provide any extra functionality during the traversal (e.g., no callbacks) - it just dumps the node, and yaml-cpp already has that functionality (the emitter can emit Yaml::Nodes)
2. It doesn't provide any mechanism for quoting strings with nonprintable or newline characters.
3. It only handles maps with strings as keys (so it doesn't recurse to keys).
So I don't think I'll add anything like this (at least for the time being). Thanks for your interest!
Hi,
I would like to tell you something about parsing time. In Release version, I can load my yaml file (about 250KB-300KB) in ~800ms. In Debug version, it's taking 30sec. Have you an idea if this could be improved?! I cannot share you the yaml file, but there is a big sequence, where each element contains a few sequences where one has many words as elements.
It's not a big big issue, but each time I am running debug mode, I have to be patient.
Thanks.
I mean the following line is the one which takes this amount of time: parser.GetNextDocument(doc);
@bobbycoul, you can compile yaml-cpp in Release mode and the rest of your program in Debug mode.
When I try the simple example with YAML 1.2 code from the spec page, I get a doc size of 0. Examples on this page work fine. Does yaml-cpp support the entire YAML 1.2 spec, or just a subset. If so, is that subset documented?
@homechicken, I just tried both examples you linked to (the invoice and the log file), and they both worked fine for me. What does your source look like?
int main(int argc, char **argv) { try { std::ifstream fin("test.yaml"); YAML::Parser parser(fin); YAML::Node doc; parser.GetNextDocument(doc); std::cout << "Doc node size is " << doc.size() << '\n'; } catch(YAML::ParserException &e) { std::cout << "YAML Exception caught at line " << e.line + 1 << ", col " << e.column + 1 << ": " << e.msg << std::endl; } return 0; }I get a document size of 0 with the YAML 1.2 spec example, but a doc size of 3 (expected) with the Ogre/Dragon/Wizard example file above. I must be missing something here, but I don't see it. Thanks for the help, and the quick response!
Ah, you mean doc.size()! If the YAML::Node is a sequence node, then that gives the number of elements in the sequence; otherwise (and in this case, it's a map node), it doesn't give any relevant info.
Aha! It's starting to make sense to me now. Sorry about the confusion, I'm just getting started with YAML, having decided to use it instead of JSON in a hobby project of mine. Thanks again!
Okay, new question. I have a map of "aliases" for a game:
I talked to the people on the #yaml IRC channel, and they confirmed that "'" should be valid YAML, but yaml-cpp doesn't seem to accept it, I get an "end of map not found" error. Will yaml-cpp allow quoted indicators as scalars? (It parses properly in the ypaste page on yaml.org.)
Now that is a bug!
I opened it up as Issue 25 , and (I think) fixed it. Please test it on your end as well - I've been known to make mistakes :)
Also, if you have any comments on that issue in particular, please write them on the issue page.
Thanks for the catch!
Thanks for the quick answer! I wasn't positive about that one being a bug (I am really new at YAML), so I thought I'd discuss it first. I'll be testing it shortly.
Hi
How can I parse this kind of document:
stats:
Tried node["stats"]["something"] but doesn't work :(
That is a map of a sequence of maps. So you need to do
Note: on these google wiki pages, enclose code in {{{ and }}} to get it to appear correctly.
Hi. I've written piece of code that was working really fine under the console. I moved it not changing anything to my game code. Unfortunately game does not start but gives an error (Runtime error, the application has requested runtime to terminate it in an unusual way). I debugged the code line by line and this error is made by this:
So my question is - what can cause parser constructor to fail? Could this have anything to do with SDL? I seriously just copy-pasted it and put it in function. Maybe linking libraries order matter?
Hmmm, that's a strange one. I don't know why it would do that. Try to move that line to a place after the game has already started, and also make sure that you recompile and relink everything. If you're feeling generous, compile yaml-cpp with no optimizations and try to debug into the constructor to see exactly where it's failing.
OMG! In this console project I added in linking fields SDL librarys, added #include "SDL.h" and changed int main() to int main(int argc, char argv) (SDL requires it) and IT DOES NOT WORK! Console window pops up but YAML parser doesn't do anything (checked with multiple examples). Do you know any reason what could it cause? Maybe you could check it with SDL?
I don't know what the problem is. I use SDL for most of my games, and the yaml-cpp works fine with it. It is difficult to get SDL up and running, so I'm assuming your problem is with that. Try debugging step by step to see where the problem is.
Just wanted to say thanks a lot for this library. I've got a fully-functioning YAML implementation in my MUD now thanks to yaml-cpp!
Hi back from vacations
I wanted to reply you about your answer:
I tried this, and also check question in msdn forums, but the problem is with libc, if I have the static lib compiled in release, it will link to libcmt.lib, and my program (debug) will link to libcmtd.lib, so it cannot work, I tried many solutions (/force:multiple, ignore libcmt.lib, but doesn't work) do you have any idea?
Bobby, you could try just enabling optimizations in the debug build
Hi evryone! I tried installing your library on my Mac, and everything looked fine. But when I actually want to compile your "BAsic Parsing" example, I get the following error:
Undefined symbols: "YAML::Node::Node()", referenced from: _main in cc6YStvz.o "YAML::Parser::operator bool() const", referenced from: _main in cc6YStvz.o "YAML::Parser::GetNextDocument(YAML::Node&)", referenced from: _main in cc6YStvz.o "YAML::Node::~Node()", referenced from: _main in cc6YStvz.o _main in cc6YStvz.o "YAML::Parser::~Parser()", referenced from: _main in cc6YStvz.o _main in cc6YStvz.o "YAML::Parser::Parser(std::basic_istream<char, std::char_traits<char> >&)", referenced from: _main in cc6YStvz.o ld: symbol(s) not found collect2: ld returned 1 exit statusMaybe it's something trivial, I am no expert. Any ideas would be very much appreciated.
Hi Olav,
Those are linker errors, which means you're not linking properly to the library. If you're compiling on the command line with gcc, you should add the option '-lyaml-cpp'. If you're using Xcode, I think you just have to drag the library (libyaml-cpp.so) into your project, but I'm not 100% sure.
Great, that worked. Thanks!
Hello,
Is there a way to replicate representer/dumper functionality of pyyaml in yaml-cpp?
Hi shaxbee,
C++ doesn't have reflection, so we can't do something as simple as in pyyaml (or in many of the YAML implementations in dynamic/duck typed languages). But you can mimic it by writing your own representer/dumper functions (see this document, and HowToEmitYAML).
Re: I know this is an old reply, but...
All istreams will only figure out that a "0x" preface is hex if the decimal flag is cleared: unsetf( ios::dec ). Unintuitive, for sure, but that's C++. =D Decimal integers will be parsed correctly too, I recommend making the change to yaml's use of stringstreams internally.
Tim, do you mean that you'd like
to be parsed as
If so, I've been meaning to do this, but putting it off. I'll get to it eventually :)
Yes, please. And all that's needed is to make a call to: stream.unsetf( ios::dec ) Thanks.
Also, I'm having trouble with a visual studio complied version of yaml-cpp where the emitter is quoting the YAML::Key's to my maps. The mingw version of the very same code does not.
Hi, I also would love to be able to parse hex values. Tim's suggestion is good, and he is correct, C++ will try to infer the type of the value being read from the stream if you unset the format flags.
However I recommend doing the following: stream >> std::resetiosflags(std::ios::basefield) >> value;
An alternative suggestion, and one that would probably be more flexible in the future, is to have your Node operator >> use boost::lexical_cast.
Something like this maybe...
template <typename T> inline void operator>>(const Node& node, T& value) { std::string scalar; node.GetScalar(scalar); value = boost::lexical_cast<T>(scalar); }Lexical cast uses operator >> and << of the type T to do the conversion. Since the user defines these operators on their own T, you offload the actual conversion work to the user.
Also, after looking a bit at your regex code, I strongly suggest you consider using boost regular expressions. Why try to reinvent the wheel? :-)
http://www.boost.org/doc/libs/1_40_0/libs/regex/doc/html/index.html http://www.boost.org/doc/libs/1_40_0/libs/conversion/index.html
I was able to modify the code to use lexical_cast, and am now able to parse hex values. Here is the code I used.
// A useful little class so you can lexical_cast hexadecimal strings to actual hex values. Implicitly may be converted to a size_t via the operator size_t(). class Hex { public: operator size_t() { return data; } friend std::ostream & operator<<(std::ostream & os, Hex & hex) { return os << std::hex << hex.data; } friend std::istream & operator>>(std::istream & is, Hex & hex) { std::string tmp; is >> tmp; std::istringstream iss(tmp); iss.exceptions(std::ios_base::badbit | std::ios_base::failbit); iss >> std::hex >> hex.data; return is; } private: size_t data; }; // Here are my modifications to include/conversion.h #pragma once #ifndef CONVERSION_H_62B23520_7C8E_11DE_8A39_0800200C9A66 #define CONVERSION_H_62B23520_7C8E_11DE_8A39_0800200C9A66 #include "null.h" #include <string> #include <sstream> #include <boost/lexical_cast.hpp> namespace YAML { template <typename T> inline bool Convert(const std::string& input, T& output) { try { output = boost::lexical_cast<T>(input); } catch (boost::bad_lexical_cast const & e) { return false; } return true; } template <> bool Convert<bool>(const std::string& input, bool& output); template <> bool Convert<_Null>(const std::string& input, _Null& output); } #endif // CONVERSION_H_62B23520_7C8E_11DE_8A39_0800200C9A66This is all of course, just food for thought! Whatever route you decide to go, please post it here, I would be interested in seeing how you solve the problem.
Thanks!
Carter
Hi Carter,
Thanks for the suggestions. Please see Issue 56 for more info on what we're doing with casting (and why a simple template Convert function won't work).
As for regex, I agree, it's silly to reinvent the wheel. However:
Anyways, I've been meaning to try the switch just to see how it performs, but #1 is pretty important to me. If (and when) boost::regex becomes part of the standard C++ library, I'll jump on it in a heartbeat.
I've been enjoying using yaml-cpp on a project I'm working on, but am stuck on parsing the following. Any suggestions would be a appreciated.
components: - !project,2008-03-06/component name: main - !project,2008-03-06/component name: Classes - !project,2008-03-06/component name: Client InterfaceThanks, Ben
Hi Ben,
That actually isn't valid YAML. First, you probably meant to add a newline between components: and the first - !project. Second, you can't have a comma in a tag shorthand. See the YAML spec section 6.9.1, specifically the subsection on "Tag Shorthands".
@Ben,
By the way, in the future, try to be more specific about your problem. You just wrote, "I am stuck on parsing the following." Are you getting an error message? Are you unsure of how to start parsing it?
Check out How To Ask Questions The Smart Way by Eric Raymond.
Sorry, code was mangled in the translation (4th time charm?). Lets hope this posts well. As for specifics, the error message is (from YAML::ParserException?):
I appreciate the link in the YAML specs. Hardest point of any new language is the spec...and the yaml specs are ... well, not reader friendly, imho.
Best, Ben
@Ben,
It looks like the issue is the comma in your tags. You can either make them verbatim tags (use !<!whatever>) or use a TAG directive to save that prefix (!project.foo.bar,2008-03-06/).
Hi guys.
So i have a YAML document that looks like this
I use the following code to parse it:
for (unsigned int i =0; i< doc.size(); i++) { Jogador * jog = new Jogador(); doc[i]>> (*jog); cout << jog->getNomeP() << endl; } ///and the overload function does this void operator >> (const YAML::Node& node, Jogador & jog) { YAML::Iterator it = node.begin(); YAML::Iterator ite = node.end(); string teste; string teste2; while (it!=ite){ it.first() >> teste; it.second() >> teste2; cout << teste << ": " << teste2 << endl; ++it; } }And this is what I get ond the console:
Why is this happening? Why is the date beeing parsed first? Am I doing something wrong?
Thanks
Ups nevermind the cout after the doc[i] >> (*jog) its commented.
In YAML, maps are unordered (so implementations can order them, or not, in any way they like). yaml-cpp alphabetizes by key (which is why I'm surprised that it doesn't parse country first - I tried your example file, and for me, it parses in the order country, date, id, name, as expected. Are you sure it's parsing date first?
In any case, if you want an ordered map, the standard way to do this is:
It doenst parse country first, because I translated the output (as I didn't knew the order was alphabetical), and in my language, date comes before country.
So, as a rule, if I have my maps like that, and I parse them with iterators, they will be sorted by key?
Thanks for the explanation, and sorry for misleading you.
Yes, with the current yaml-cpp implementation, they are sorted by key. But this isn't required by the spec, and it may change in the future, so you shouldn't depend on it.
Is there a way to speed up parsing if I know the structure of the document beforehand (e.g. it will be a sequence of maps, and each of the maps has the same three keys) ?
Right now, a yaml file with 10000 sequence elements such as this:
takes around 8 seconds to parse (quad-core 2.8ghz) which seems like a lot to me.
Thanks!
@baeuml.kit,
No, unfortunately not. I'm planning to work on speeding the library up over the next month or two, when I get time, but there are two fundamental slow points during parsing:
1. The actual parsing (i.e., matching expressions), which I believe I can speed up
2. String copying, which I'm not so sure about. yaml-cpp will necessarily be slower than libyaml because libyaml produces events as it parses, and so it doesn't need to copy all of the strings (I believe - I haven't looked at the source in a while). yaml-cpp, on the other hand, "represents" the content after it parses, which currently copies everything over. That said, lazy copying will certainly speed things up here, and I plan to do that.
Hi there - I've recently downloaded yaml-cpp and started experimenting with it. (I'm planning on using it for data definition in a game I'm writing.) So far, it looks pretty promising! I did have a couple of observations though:
Firstly, I was wondering whether you're planning on adding something like a GetTag?() method on the YAML::Node class? This would be useful for validating the types of input data. For example, if I have a yaml document like this:
--- !Vector2 { x: 10., y: 20. } ...Then I can do something like this in the input operator:
struct Vector2 { float x, y }; void operator>>(const YAML::Node &n, Vector2 &v) { assert(std::string("!Vector2") == n.GetTag()); //not the type we expected! n["x"] >> v.x; n["y"] >> v.y; }I did a quick experiment, adding an implementation for GetTag? in node.h like so:
const std::string &GetTag() const { return m_tag; }which actually seems to work for my purposes. In "production code", throwing an exception would probably be a better course of action than an assertion, but the idea is that I'd be able to double-check that the tags in my YAML data match the types being instantiated at runtime. (I wouldn't consider myself anything more than a YAML novice, so if I'm completely wrong about how tags are supposed to work, please feel free to correct me!)
Secondly, I was wondering if you'd consider moving the headers in the include/ directory into a subdirectory (eg: include/yaml-cpp/ or similar)? This effectively puts the headers into their own "namespace" - it's a fairly common practice for C and C++ libraries, which reduces the chance of an include directive causing the wrong header to be included. This is just a little insurance in case I had a "null.h" or a "mark.h" in my application code (or another third-party library), for example, I could end up including the yaml-cpp headers unintentionally, whereas with this directive:
there's a lot less ambiguity over which header should be included.
Anyway, these are pretty minor issues, but I thought I'd mention them, in the interests of giving some feedback. I'll definitely be interested to see how yaml-cpp develops - YAML looks like a great language for data definition, and it's great to have an open-source implementation available in C++. Keep up the good work! :)
@jonhtcphone,
There actually is a GetTag() method in the latest revision (if you check out with svn) - it's pretty close to pushing to a new version (there's just one more thing I want to do beforehand), and the new tag stuff is pretty solid. (There's actually quite a bit of difference between the last tagged version and the latest revision; tags were not parsed correctly, and now that's fixed. But the GetTag() method is essentially (possibly exactly, I don't recall) what you wrote.)
As for the include files, the make install command moves them into a include/yaml-cpp folder, so it effectively does work that way. I recommend using this, but I suppose perhaps it might be more consistent to keep them in a yaml-cpp subfolder in the source? What do you think?
Hi - it's nice to know that there's a GetTag?() coming to a public release soon - I'll be happy to wait for the next official release and I'll definitely give it a go. (And it's good to know that I wasn't completely misguided in how it would work too!)
I have to admit that I hadn't tried using the supplied makefiles, so I missed the install stage (my bad!). I generally prefer to include third-party libraries directly into my source tree, and build them from source along with my application code. (It's a slightly idiosyncratic way of working, but I find it tends to reduce the amount of pre-configuration required when trying to build my game from scratch on a fresh machine, or porting to a new OS, amongst other reasons.)
I can probably move the headers locally, but having said that, I prefer not to modify library code unless I absolutely have to, so if you did move the headers into include/yaml-cpp, it would make my life a tiny bit easier. And it would be a little more consistent, but I guess I'll leave it up to you ;)
Looking forward to the next release!
I would be interested to know which source files are used by the emitter, by the parser, or by both of them.
Actually, I would like to have the code base divided into subdirectories emitter, parser, common - and I would like a configuration option that allows me to produce just a parser library. Admittedly, this is pretty much a question of taste.
I apologize if this is the wrong place to ask this. I went looking for an email address and couldn't find one. I'm seeing some behavior and I'm not sure if it's correct or not. I've looked through the yaml docs and I couldn't really determine for sure so I thought I'd ask here.
This is the code I'm using to extract the yaml values:
` YAML::Parser parser(fin);
when parsing this input it fails with the following error: yaml-cpp: error at line 5, column 10: illegal map value.
here's the file:
`invoice : 34843 date : 2001-01-23 billto : me shipto :you product : stuff tax : alot total : evenmore comments : yoyoyo`
however this input doesn't fail (I adapted it from http://www.yaml.org/start.html)
invoice: 34843 date : 2001-01-23 bill-to: &id001 ship-to: id001 tax : 251.42 total: 4443.52
My question to you is, is this expected behavior? Is the first one really invalid yaml? I don't know yaml deeply enough to know for sure. If it isn't I'd appreciate an explanation. If it is valid yaml I'm willing to hunt down the issue, I just want to make sure the mistake isn't on my end.
Ultimately if yaml is this brittle I'm probably going to go with something else for my configuration files. My needs are currently pretty simple and I'd prefer not requiring the users of my software to have to worry about the exact spacing of the configuration files.
ormatting is bad so let me try that again.
fails:
doesn't fail:
the code used to extract the values.
YAML::Parser parser(fin); YAML::Node doc; parser.GetNextDocument(doc); for(YAML::Iterator it = doc.begin(); it != doc.end(); ++it) { std::string key,value; it.first() >> key; it.second() >> value; map[key] = value; }@Michael,
The problem is with your line
YAML requires a space after the colon. This isn't as weird (or brittle) as you think - in most cases spaces (except for initial indents) doesn't matter. The question is, how can you tell if the colon is part of the word you or not? From the YAML spec:
Hi, guys. There's my question. All examples of yaml parsing in this tutorial(and parsing examples with source) are presented as direct-access to the field(struct, map, vector etc.). Like that -
But what am I supposed to do if I work with object of some class with fields declared private, and getters and setters for access means? It's stupid to redo the whole project to use public fields just to be able of using deserialization. But what's the solution? Is there any? Of course I can do
But this is ugly as hell.
@Maestro,
One thing you could do is make a member function
where you do all of your deserializing, and then overload
void operator >> (const YAML::Node& node, SomeClass& obj) { obj.Load(node); }Oh, thanks. I'll try that.
Hello all, and sorry for asking trivial questions, but I am new to YAML (and to C++ for that matter). I have a problem with hash merges that I have not been able to resolve. I've written a code to read a file like this one:
- channel: input units: &input length: Angstrom energy: eV mass: atomic_weight - channel: output units: length: Angstrom energy: eV mass: atomic_weightMy code parses and reads this fine. However, if I try to use an anchor like so:
- channel: input units: &input length: Angstrom energy: eV mass: atomic_weight - channel: output units: <<: *inputit fails after reading the first item in the channel list, with message: terminate called after throwing an instance of 'YAML::TypedKeyNotFound?<std::string>'
It does look like yaml-cpp is not substituting the anchored information in the second item of the channel list.
Any hints? Thanks in advance.
If the format is being produced by software then I could agree with you about it not being brittle.
bill-to :you vs bill-to: you
when it's being formatted by a human is another matter. I'm afraid I can't agree with that not being brittle.
I suppose I'll be using the tried and true ini file or rolling my own solution.
Thanks for the info jbeder, perhaps I'll find a use for yaml somewhere else.
@emmabb,
Merging isn't implemented yet in yaml-cpp (see Issue 41). If you just write
It should work, but you won't be able to add any other key/value pairs like you would with a merge.
(By the way, the TypedKeyNotFound exception is thrown because you tried to access a node with a key that didn't exist. Presumably you wrote
so it actually parsed fine.)
@jbeder,
Your suggestion did indeed work fine. I was not aware of the issue with merging not being yet implemented. Thanks a lot for your help!
If I have a mapping {A: "a", B: a}, it parses "a" as a(double quotes are eaten up). I need the double quotes in the scalar. I could not locate any API to prevent this. Please help. Thanks in advance.
@kumarsarth
That's YAML. If you want a string with double quotes, you'll have to escape them:
{A: "\"a\"", B: a}By the way, in the future, please post questions on http://stackoverflow.com and tag them as yaml-cpp.
@jbeder, Thanks for the answer.
I took the monster YAML example, and expanded on it further. It will parse the YAML into records, and then use those records to emit new YAML. You can find the code here.
#include "yaml.h" #include <iostream> #include <fstream> #include <string> #include <vector> // our data models struct Vec3 { float x, y, z; }; struct Power { std::string name; int damage; }; struct Monster { std::string name; Vec3 position; std::vector <Power> powers; }; // operators for parsing void operator >> (const YAML::Node& node, Vec3& v) { node[0] >> v.x; node[1] >> v.y; node[2] >> v.z; } void operator >> (const YAML::Node& node, Power& power) { node["name"] >> power.name; node["damage"] >> power.damage; } void operator >> (const YAML::Node& node, Monster& monster) { node["name"] >> monster.name; node["position"] >> monster.position; const YAML::Node& powers = node["powers"]; for(unsigned i = 0; i < powers.size(); i++) { Power power; powers[i] >> power; monster.powers.push_back(power); } } // operators for emitting 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; } YAML::Emitter& operator << (YAML::Emitter& out, const Power& power) { out << YAML::BeginMap; out << YAML::Key << "name"; out << YAML::Value << power.name; out << YAML::Key << "damage"; out << YAML::Value << power.damage; out << YAML::EndMap; return out; } YAML::Emitter& operator << (YAML::Emitter& out, const Monster& monster) { out << YAML::BeginMap; out << YAML::Key << "name"; out << YAML::Value << monster.name; out << YAML::Key << "position"; out << YAML::Value << monster.position; out << YAML::Key << "powers"; out << YAML::Value; out << YAML::BeginSeq; for(size_t pidx = 0; pidx < monster.powers.size(); ++pidx) { out << monster.powers[pidx]; } out << YAML::EndSeq; out << YAML::EndMap; return out; } int main() { std::string monsters_yaml("- name: Ogre\n position: [0, 5, 0]\n powers:\n - name: Club\n damage: 10\n - name: Fist\n damage: 8\n- name: Dragon\n position: [1, 0, 10]\n powers:\n - name: Fire Breath\n damage: 25\n - name: Claws\n damage: 15\n- name: Wizard\n position: [5, -3, 0]\n powers:\n - name: Acid Rain\n damage: 50\n - name: Staff\n damage: 3\n"); // list of monsters std::vector<Monster> monsters; // write out the monster.yaml std::ofstream monsters_yaml_file; monsters_yaml_file.open("monsters.yaml"); monsters_yaml_file << monsters_yaml; monsters_yaml_file.close(); // read in the the monster.yaml std::ifstream fin("monsters.yaml"); YAML::Parser parser(fin); YAML::Node doc; parser.GetNextDocument(doc); for(size_t i = 0; i < doc.size(); i++) { Monster monster; doc[i] >> monster; monsters.push_back(monster); std::cout << monster.name << std::endl; } YAML::Emitter out; out << YAML::BeginSeq; for(size_t i = 0; i < monsters.size(); i++) { out << monsters[i]; } out << YAML::EndSeq; std::cout << out.c_str() << std::endl; return 0; }Does yaml-cpp handle block order ?
I mean, if I write this :
B: 4 A: 2
Will I get B then A (and not A then B) ?
@iamphpman,
In YAML, mapping keys are unordered. So there's no guarantee what you'll get. If you want to mimic an ordered map, do:
(This is just a sequence of single key/value pairs, but it is analogous to an ordered map.)
By the way, in the future, please post questions on http://stackoverflow.com and tag them as yaml-cpp.
Ok, thank you (and sorry for the bad post). =)
Hello,
I was wondering if there was a way to read YAML more than one YAML documents and extract the information from it. For example I have a YAML file called nod.yaml and included in this file is - Joint ID: 0
- Joint ID: 0 --- - Joint ID: 0 --- - Joint ID: 0 - Joint ID: 0 --- - Joint ID: 0 - Joint ID: 0 --- - Joint ID: 0 - Joint ID: 0 - Joint ID: 0 - Joint ID: 0 - Joint ID: 0how do I read each document?
@charles,
It's right at the top of the page, under "Basic Parsing":
YAML::Node doc; while(parser.GetNextDocument(doc)) { // ... }By the way, in the future, please post questions on http://stackoverflow.com and tag them as yaml-cpp.
When parsing yaml file, normally we get root node from parser.
And I'm wondering if I can reference the root node after parsing process. Like below.
YAML::Node* globalRoot; void ParseDocument(filename) { YAML::Parser parser(fin) parser.GetNextDocument(*globalRoot); } void myFunction() { ParseDocument("myYAML.yml"); // After the method above, we lose the parser instance since it's a local variable. // But if all child data is copied, below code should be safe. // If globalRoot is just pointing inside the parser, this could be dangerous. std::string stringKey; (*globalRoot)["myKey"] >> stringKey; }Can I use like code above??
I've little problem with getting to Vec3 content. Any help would be appreciated.
@icepop, if you post a question with more details on stackoverflow.com and tag it yaml-cpp, I'll do my best to answer it!
Problem with exceptions.h, specifically the const initialization of the std::string literals. I believe the C++ standard states that only integer or enum constants can be initialized in the declaration.
Is it possible to get an handle to the current position/context of the parser i.e. the line no. and the column ? I need to impose some extra restrictions in the yaml file syntax and print errors if the same are violated giving the line-no information.
@TheReclu?:
1. I'm not sure what you mean, exactly - do you want to change the actual parsing algorithm? If so, that's not possible externally, but feel free to modify the code to your heart's content. Look in scanner.cpp and scantoken.cpp for details.
2. If you want to know where a particular node starts, call the GetMark?() function (it's just Mark() in the trunk).
3. Please post future questions on stackoverflow.com and tag them yaml-cpp :)
Thanks!
Is reading from base64 not supported?
@t.hiroya, no there isn't currently a built-in way to read base64 data. If you want to write one, I'd be happy to accept a patch :)
Can I override operator>> for bool?
void operator>> (const YAML::Node & node, bool & b) { // I'd like it to parse node as string "yes"/"no", "1"/"0" }Hello,
is it possible to search for comments and parse them, too?