How-To-Parse-A-Document-(Old-API).md (7076B)
1 _The following describes the old API. For the new API, see the [Tutorial](https://github.com/jbeder/yaml-cpp/wiki/Tutorial)._ 2 3 ## Contents ## 4 5 6 # Basic Parsing # 7 8 The 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: 9 10 ``` 11 #include <fstream> 12 #include "yaml-cpp/yaml.h" 13 14 int main() 15 { 16 std::ifstream fin("test.yaml"); 17 YAML::Parser parser(fin); 18 19 YAML::Node doc; 20 while(parser.GetNextDocument(doc)) { 21 // ... 22 } 23 24 return 0; 25 } 26 ``` 27 28 # Reading From the Document # 29 30 Suppose we have a document consisting only of a scalar. We can read that scalar like this: 31 32 ``` 33 YAML::Node doc; // let's say we've already parsed this document 34 std::string scalar; 35 doc >> scalar; 36 std::cout << "That scalar was: " << scalar << std::endl; 37 ``` 38 39 How about sequences? Let's say our document now consists only of a sequences of scalars. We can use an iterator: 40 41 ``` 42 YAML::Node doc; // already parsed 43 for(YAML::Iterator it=doc.begin();it!=doc.end();++it) { 44 std::string scalar; 45 *it >> scalar; 46 std::cout << "Found scalar: " << scalar << std::endl; 47 } 48 ``` 49 50 ... or we can just loop through: 51 52 ``` 53 YAML::Node doc; // already parsed 54 for(unsigned i=0;i<doc.size();i++) { 55 std::string scalar; 56 doc[i] >> scalar; 57 std::cout << "Found scalar: " << scalar << std::endl; 58 } 59 ``` 60 61 And finally maps. For now, let's say our document is a map with all keys/values being scalars. Again, we can iterate: 62 63 ``` 64 YAML::Node doc; // already parsed 65 for(YAML::Iterator it=doc.begin();it!=doc.end();++it) { 66 std::string key, value; 67 it.first() >> key; 68 it.second() >> value; 69 std::cout << "Key: " << key << ", value: " << value << std::endl; 70 } 71 ``` 72 73 Note that dereferencing a map iterator is undefined; instead, use the `first` and `second` methods to get the key and value nodes, respectively. 74 75 Alternatively, we can pick off the values one-by-one, if we know the keys: 76 77 ``` 78 YAML::Node doc; // already parsed 79 std::string name; 80 doc["name"] >> name; 81 int age; 82 doc["age"] >> age; 83 std::cout << "Found entry with name '" << name << "' and age '" << age << "'\n"; 84 ``` 85 86 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. 87 88 ## Optional Keys ## 89 90 If you try to access a key that doesn't exist, `yaml-cpp` throws an exception (see [When Something Goes Wrong](https://github.com/jbeder/yaml-cpp/wiki/How-To-Parse-A-Document-(Old-API)#When_Something_Goes_Wrong). If you have optional keys, it's often easier to use `FindValue` instead of `operator[]`: 91 92 ``` 93 YAML::Node doc; // already parsed 94 if(const YAML::Node *pName = doc.FindValue("name")) { 95 std::string name; 96 *pName >> name; 97 std::cout << "Key 'name' exists, with value '" << name << "'\n"; 98 } else { 99 std::cout << "Key 'name' doesn't exist\n"; 100 } 101 ``` 102 103 # Getting More Complicated # 104 105 The 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: 106 107 ``` 108 struct Vec3 { 109 float x, y, z; 110 }; 111 ``` 112 113 We can read this in one operation by overloading the extraction (>>) operator: 114 115 ``` 116 void operator >> (const YAML::Node& node, Vec3& v) 117 { 118 node[0] >> v.x; 119 node[1] >> v.y; 120 node[2] >> v.z; 121 } 122 123 // now it's a piece of cake to read it 124 YAML::Node doc; // already parsed 125 Vec3 v; 126 doc >> v; 127 std::cout << "Here's the vector: (" << v.x << ", " << v.y << ", " << v.z << ")\n"; 128 ``` 129 130 # A Complete Example # 131 132 Here's a complete example of how to parse a complex YAML file: 133 134 `monsters.yaml` 135 136 ``` 137 - name: Ogre 138 position: [0, 5, 0] 139 powers: 140 - name: Club 141 damage: 10 142 - name: Fist 143 damage: 8 144 - name: Dragon 145 position: [1, 0, 10] 146 powers: 147 - name: Fire Breath 148 damage: 25 149 - name: Claws 150 damage: 15 151 - name: Wizard 152 position: [5, -3, 0] 153 powers: 154 - name: Acid Rain 155 damage: 50 156 - name: Staff 157 damage: 3 158 ``` 159 160 `main.cpp` 161 162 ``` 163 #include "yaml-cpp/yaml.h" 164 #include <iostream> 165 #include <fstream> 166 #include <string> 167 #include <vector> 168 169 // our data types 170 struct Vec3 { 171 float x, y, z; 172 }; 173 174 struct Power { 175 std::string name; 176 int damage; 177 }; 178 179 struct Monster { 180 std::string name; 181 Vec3 position; 182 std::vector <Power> powers; 183 }; 184 185 // now the extraction operators for these types 186 void operator >> (const YAML::Node& node, Vec3& v) { 187 node[0] >> v.x; 188 node[1] >> v.y; 189 node[2] >> v.z; 190 } 191 192 void operator >> (const YAML::Node& node, Power& power) { 193 node["name"] >> power.name; 194 node["damage"] >> power.damage; 195 } 196 197 void operator >> (const YAML::Node& node, Monster& monster) { 198 node["name"] >> monster.name; 199 node["position"] >> monster.position; 200 const YAML::Node& powers = node["powers"]; 201 for(unsigned i=0;i<powers.size();i++) { 202 Power power; 203 powers[i] >> power; 204 monster.powers.push_back(power); 205 } 206 } 207 208 int main() 209 { 210 std::ifstream fin("monsters.yaml"); 211 YAML::Parser parser(fin); 212 YAML::Node doc; 213 parser.GetNextDocument(doc); 214 for(unsigned i=0;i<doc.size();i++) { 215 Monster monster; 216 doc[i] >> monster; 217 std::cout << monster.name << "\n"; 218 } 219 220 return 0; 221 } 222 ``` 223 224 # When Something Goes Wrong # 225 226 ... 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`: 227 228 ``` 229 try { 230 std::ifstream fin("test.yaml"); 231 YAML::Parser parser(fin); 232 YAML::Node doc; 233 parser.GetNextDocument(doc); 234 // do stuff 235 } catch(YAML::ParserException& e) { 236 std::cout << e.what() << "\n"; 237 } 238 ``` 239 240 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: 241 242 ``` 243 YAML::Node node; 244 YAML::NodeType::value type = node.Type(); // should be: 245 // YAML::NodeType::Null 246 // YAML::NodeType::Scalar 247 // YAML::NodeType::Sequence 248 // YAML::NodeType::Map 249 ``` 250 251 # Note about copying `YAML::Node` # 252 253 Currently `YAML::Node` is non-copyable, so you need to do something like 254 255 ``` 256 const YAML::Node& node = doc["whatever"]; 257 ``` 258 259 This is intended behavior. If you want to copy a node, use the `Clone` function: 260 261 ``` 262 std::auto_ptr<YAML::Node> pCopy = myOtherNode.Clone(); 263 ``` 264 265 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.