My favorites | Sign in
Project Home Downloads Wiki Issues Source
Search
for
Tutorial  
Quick Start Tutorial
Updated Jan 28, 2012 by jbe...@gmail.com

Introduction

The following describes a new, experimental API. For the old API, see HowToParseADocument or HowToEmitYAML.

This API is very much in flux, and I'd love to get feedback. Please email me or leave a comment!

A typical example, loading a configuration file, might look like this:

YAML::Node config = YAML::LoadFile("config.yaml");

if(config["lastLogin"])
   std::cout << "Last logged in: " << config["lastLogin"].as<DateTime>() << "\n";

const std::string username = config["username"].as<std::string>();
const std::string password = config["password"].as<std::string>();
login(username, password);
config["lastLogin"] = getCurrentDateTime();

std::ofstream fout("config.yaml");
fout << config;

Basic Parsing and Node Editing

All nodes in a YAML document (including the root) are represented by YAML::Node. You can check what kind it is:

   YAML::Node node = YAML::Load("[1, 2, 3]");
   assert(node.Type() == YAML::NodeType::Sequence);
   assert(node.IsSequence());  // a shortcut!

Collection nodes (sequences and maps) act somewhat like STL vectors and maps:

YAML::Node primes = YAML::Load("[2, 3, 5, 7, 11]");
for(std::size_t i=0;i<primes.size();i++)
  std::cout << primes[i].as<int>() << "\n";
// or:
for(YAML::const_iterator it=primes.begin();it!=primes.end();++it)
   std::cout << it->as<int>() << "\n";

primes.push_back(13);
assert(primes.size() == 6);

and

YAML::Node lineup = YAML::Load("{1B: Prince Fielder, 2B: Rickie Weeks, LF: Ryan Braun}");
for(YAML::const_iterator it=lineup.begin();it!=lineup.end();++it)
    std::cout << "Playing at " << it->first.as<std::string>() << " is " << it->second.as<std::string>() << "\n";

lineup["RF"] = "Corey Hart";
lineup["C"] = "Jonathan Lucroy";
assert(lineup.size() == 5);

Querying for keys does not create them automatically (this makes handling optional map entries very easy)

    YAML::Node node = YAML::Load("{name: Brewers, city: Milwaukee}");
    if(node["name"])
       std::cout << node["name"].as<std::string>() << "\n";
    if(node["mascot"])
       std::cout << node["mascot"].as<std::string>() << "\n";
    assert(node.size() == 2); // the previous call didn't create a node

Building Nodes

You can build YAML::Node from scratch:

YAML::Node node;  // starts out as null
node["key"] = "value";  // it now is a map node
node["seq"].push_back("first element");  // node["seq"] automatically becomes a sequence
node["seq"].push_back("second element");

node["mirror"] = node["seq"][0];  // this creates an alias
node["seq"][0] = "1st element";  // this also changes node["mirror"]
node["mirror"] = "element #1";  // and this changes node["seq"][0] - they're really the "same" node

node["self"] = node;  // you can even create self-aliases
node[node["mirror"]] = node["seq"];  // and strange loops :)

The above node is now:

&1
key: value
&2 seq: [&3 "element #1", second element]
mirror: *3
self: *1
*3 : *2

How Sequences Turn Into Maps

Sequences can be turned into maps by asking for non-integer keys. For example,

YAML::Node node  = YAML::Load("[1, 2, 3]");
node[1] = 5;  // still a sequence, [1, 5, 3]
node.push_back(-3) // still a sequence, [1, 5, 3, -3]
node["key"] = "value"; // now it's a map! {0: 1, 1: 5, 2: 3, 3: -3, key: value}

Indexing a sequence node by an index that's not in its range will usually turn it into a map, but if the index is one past the end of the sequence, then the sequence will grow by one to accommodate it. (That's the only exception to this rule.) For example,

YAML::Node node = YAML::Load("[1, 2, 3]");
node[3] = 4; // still a sequence, [1, 2, 3, 4]
node[10] = 10;  // now it's a map! {0: 1, 1: 2, 2: 3, 3: 4, 10: 10}

Converting To/From Native Data Types

Yaml-cpp has built-in conversion to and from most built-in data types, as well as std::vector, std::list, and std::map. The following examples demonstrate when those conversions are used:

YAML::Node node = YAML::Load("{pi: 2.718, [0, 1]: integers}");

// this needs the conversion from Node to double
double pi = node["pi"].as<double>();

// this needs the conversion from double to Node
node["e"] = 2.7818;

// this needs the conversion from Node to std::vector<int> (*not* the other way around!)
std::vector<int> v;
v.push_back(0);
v.push_back(1);
std::string str = node[v].as<std::string>();

To use yaml-cpp with your own data types, you need to specialize the YAML::convert<> template class. For example, suppose you had a simple Vec3 class:

struct Vec3 { double x, y, z; /* etc */ };

You could write

namespace YAML {
   template<>
   struct convert<Vec3> {
      static Node encode(const Vec3& rhs) {
         Node node;
         node.push_back(rhs.x);
         node.push_back(rhs.y);
         node.push_back(rhs.z);
         return node;
      }

      static bool decode(const Node& node, Vec3& rhs) {
         if(!node.IsSequence())
            return false;
         if(!node.size() == 3)
            return false;

         rhs.x = node[0].as<double>();
         rhs.y = node[1].as<double>();
         rhs.z = node[2].as<double>();
         return true;
      }
   };
}

Then you could use Vec3 wherever you could use any other type:

   YAML::Node node = YAML::Load("start: [1, 3, 0]");
   Vec3 v = node["start"].as<Vec3>();
   node["end"] = Vec3(2, -1, 0);
Comment by m...@riedel-privat.de, Nov 13, 2011

Sorry, I shouldn't have used the issue category for my problem. This example is not working for me. Is there an API change in this regard?

    YAML::Node node = YAML::Load("{name: Brewers, city: Milwaukee}");
    if(node["name"])
       std::cout << node["name"].as<std::string>() << "\n";
    if(node["mascot"])
       std::cout << node["mascot"].as<std::string>() << "\n";
    assert(node.size() == 2); // the previous call didn't create a node
Comment by project member jbe...@gmail.com, Nov 13, 2011

No problem - I answered the issue, and I'll repeat here: this was my mistake - I forgot to implement it. I've added it, r68cecbc525f9.

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

By the way, thanks for trying the new API! Given that this problem has been there since I started the new API and no one else has commented suggests that no one's trying it. I appreciate your help in getting the bugs out!

Comment by m...@riedel-privat.de, Nov 13, 2011

No I love, the struct convert<_T> ability. That's exactly what I need. Btw. push_back was replaced by append?

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

Yeah, I switched it to append. Which do you prefer? I've always felt like push_back is a silly name. But it might be better to keep it in line with the STL.

Comment by m...@riedel-privat.de, Nov 14, 2011

No no, append is definitely better. I think it's rather an advantage to not be confused with STL.

Comment by m...@riedel-privat.de, Nov 16, 2011

One more thing. the as() Conversion does not throw YAML::Exception but std::runtime_error. See code below, where I actually have to check for runtime_error.

Is this temporary. Is there a reason why it is not handled by YAML::Exceptions? Thanks.

	template < class _T >
	static bool parseNodeToValue(const YAML::Node& input, _T& value) {
		bool success = true;
		try {
			value = input.as<_T>();
			//input >> value;
		} catch (YAML::Exception &e) {
			ROS_ERROR_STREAM("Error converting from YAML! " << e.what());
			success = false;
		} catch (std::runtime_error &e) {
			ROS_ERROR_STREAM("Error converting from YAML! " << e.what());
			success = false;
		}

		return success;
	}
Comment by project member jbe...@gmail.com, Nov 16, 2011

Yeah, this was me being lazy, sorry. It will throw a YAML::Exception soon.

Comment by mokag...@gmail.com, Nov 25, 2011

IMHO this API is a great improvement compared to the old one. One suggestion would be to add the possibility of a default return value to the "as" function, so that for example node.as<int> (42) doesn't throw exceptions, but returns 42 if no value could be parsed. I think that could ease things in quite some situations. Keep up the good work!

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

@mokaga42, great idea! I posted it as  Issue 136 , and I'll get to it soon; you can keep track of it there.

Comment by dan.el...@gmail.com, Jan 21, 2012

While I also think push_back is a silly name, push_back will allow it to play better with the STL (e.g. algorithms for STL containers or std::back_inserter.)

Comment by project member jbe...@gmail.com, Jan 21, 2012

@dan, you're probably right, I renamed it back to push_back

Comment by deledrius, Jan 28, 2012

I love the new API and hope it makes it into the master branch soon!

I did need to modify the syntax for the templates as mentioned on MSDN for compiler error C2906 (http://msdn.microsoft.com/en-us/library/csxsafs9%28v=vs.100%29.aspx)

So instead, it would appear thus:

namespace YAML { 
   template<> struct convert<Vec3> {
      static Node encode(const Vec3& rhs) {
         ...
      }
      static bool decode(const Node& node, Vec3& rhs) {
         ...
      }      
   };
}
Comment by project member jbe...@gmail.com, Jan 28, 2012

@deledrius, thanks! Sorry about that typo - I fixed it in the tutorial.

Glad you like the new API!

Comment by jinserk....@gmail.com, Jan 29, 2012

Hello, I'm newbie of yaml_cpp, as well as YAML itself. first I really appreciate you for this great library. I wonder that this new API seems so different from the old one, so when you'd like to apply it as the main branch? If it's not so far from now, I want to get the new one even if it's a little buggy.

Thank you again.

Comment by project member jbe...@gmail.com, Jan 29, 2012

@jinserk, thanks! I'm planning to release this branch as 0.5.0 soon, and shortly thereafter, I'll switch this to the main branch of the repo and create a branch old-api. As for more specific time frames, I'm not sure. You can certainly grab this branch now, though, as-is :)

Comment by jinserk....@gmail.com, Jan 30, 2012

One more question, should I get boost for new api? The old one didn't require boost when I did cmake, however, new api does now. Typically boost is too heavy for a lightweight application, I want to avoid to get it. In this case is the old api the unique option I can choose?

Comment by project member jbe...@gmail.com, Jan 30, 2012

@jinserk, yes, the new API requires boost. But it only requires boost headers, no libraries, so it shouldn't be a strenuous requirement. If you do not want to include boost headers, then yes, you'll need to stick with the old API - sorry.

Comment by rkj...@gmail.com, Apr 18, 2012

I think the signature of decode:

static bool decode(const Node& node, Vec3& rhs)

is problematic because it is impossible to use with immutable classes. I think it would be better to mirror the signature of encode, i.e.,

static Vec3 decode(const Node& node)

and have the function throw an exception if there was an error in the data. This way, Vec3 can be any copyable or moveable type, and it doesn't have to be default constructable or mutable.

Comment by project member jbe...@gmail.com, Apr 18, 2012

@rkjnsn, you might be right, I'll think about that. I suppose the worry is that a type might be expensive to copy.

I'll consider both ways - thanks for the suggestion!

Comment by wwwi...@gmail.com, Apr 18, 2012

@rkjnsn, This problem occurred in my project, too. I cannot implement default constructors of required classes in some reason. So I'm unable to use the suggested solution with the

static bool decode(const Node& node, Vec3& rhs)

Please find a way to solve this! :)

Comment by rkj...@gmail.com, Apr 18, 2012

@jbeder, on most modern compilers, wouldn't return value optimization eliminate the copy, anyway?

Comment by project member jbe...@gmail.com, Apr 18, 2012

@rkjnsn, yes, that's probably true

Comment by stuart.d...@gmail.com, Apr 26, 2012

The new API is much more compact and easier to use, thanks!

I could be wrong here, but doesn't YAML allow a node to be both a scalar and a sequence/map? In the new API, YAML::Node::Type() only allows one or the other. I'm having an issue in the new API where with a document like below, the node returned from Load() is only a scalar:

"Scalar String"
- 1
- 2
- 3

I can't get at the sequence 1, 2, 3 because the YAML::Node::size() function returns zero.

Comment by project member jbe...@gmail.com, Apr 26, 2012

@stuart, thanks!

YAML doesn't allow a node to be both a scalar and a sequence/map, so your example is actually an ill-formed document.

I'm not sure what you're trying to do, but you can mimic this behavior by adding a tag to your sequence. E.g.:

!foo
- 1
- 2
- 3
Comment by stuart.d...@gmail.com, Apr 27, 2012

Interesting. Easy enough to fix the document. I assumed since it worked in the old API, it was supported by YAML. I was using the scalar as a document title, but I just turned it into a map instead:

title: "Scalar String"
data:
- 1
- 2
- 3
Comment by stuart.d...@gmail.com, Apr 27, 2012

You explain how easy it is to construct YAML with the new API, but how do you emit (write) YAML text? When I call as<std::string>() on a map or sequence, it raises an exception that it must be a scalar. Did I miss something?

Comment by stuart.d...@gmail.com, Apr 27, 2012

Never mind, looks like the old method still works. Example:

    void invalidNode( const YAML::Node &node )
    {
        YAML::Emitter out;
        out << node;
        printf( "Invalid node:\n%s", out.c_str() );
    }
Comment by project member jbe...@gmail.com, Apr 27, 2012

@stuart, that's right, but there's a shortcut as well:

std::string str = YAML::Dump(node);

I should add that to the tutorial :)

Comment by stuart.d...@gmail.com, May 4, 2012

Thanks!! BTW, the STL-like iterators and such are nice, but it doesn't quite go as far as to be compatible with things like boost Range and, by association, BOOST_FOREACH, which I use all the time to make container iteration very easy to code:

  #include <boost/foreach.hpp>
  // This does not compile:
  BOOST_FOREACH( const YAML::Node &item, node ) // node is a const YAML::Node
  {
     cout << YAML::Dump(item) << std::endl;
     ...
  }
Comment by project member jbe...@gmail.com, May 8, 2012

@stuart, interesting! I posted this as  Issue 159 , so you can follow it - I'll see if I can fix it.


Sign in to add a comment
Powered by Google Project Hosting