class mrpt::containers::yaml
Powerful YAML-like container for possibly-nested blocks of parameters or any arbitrary structured data contents, including documentation in the form of comments attached to each node.
Supports parsing from YAML or JSON streams, files, or text strings.
This class holds the root “node” in a YAML-like tree structure. Each tree node can be of one of these types:
Scalar values (“leaf nodes”): Can hold any type, stored as C++17 std::any.
Sequence container.
Map (“dictionary”): pairs of
name: value
.Null, empty nodes: yaml
~
ornull
.
Sequences and dictionaries can hold, in turn, any of the four types above, leading to arbitrarialy-complex nested structures.
This class was designed as a lightweight, while structured, way to pass arbitrarialy-complex parameter blocks but can be used to load and save YAML files or as a database.
yaml can be used to parse YAML (v1.2) or JSON streams, and to emit YAML. It does not support event-based parsing. The parser uses Pantelis Antoniou’s awesome libfyaml, which passes the full YAML testsuite.
Known limitations:
Parsing comments is limited to right-hand comments for sequence or map entries.
See examples below (containers_yaml_example/test.cpp):
#include <mrpt/containers/yaml.h> #include <mrpt/core/demangle.h> // demangle() utility #include <iostream> void YamlTest_1() { std::cout << "==== YamlTest_1 ====\n"; // Load from file: // const auto p = mrpt::containers::yaml::FromFile("xxx.yaml"); // or load from text block: // const auto p = mrpt::containers::yaml::FromText(txt); // or build a document programatically: mrpt::containers::yaml p; p["K"] = 2.0; p["N"] = 10; p["name"] = "Foo"; p["enabled"] = true; p["books"] = mrpt::containers::yaml::Sequence(); p["books"].push_back("The Hobbit"); p["books"].push_back(10.0); // You can use {}-initializers as well: p["movies"] = mrpt::containers::yaml::Sequence( {mrpt::containers::yaml::Map({{"title", "xxx"}, {"year", 2001}}), mrpt::containers::yaml::Map({{"title", "yyy"}, {"year", 1986}})}); std::cout << "K=" << p["K"] << " N=" << p["N"] << "\n"; std::cout << "name=" << p["name"] << "\n"; std::cout << "Movie year=" << p["movies"](1)["year"] << "\n"; // Get a value, or default if not found. // YAMLCPP equivalent: p["bar"].as<std::string>("none") std::cout << "bar=" << p.getOrDefault<std::string>("bar", "none") << "\n"; // Iterate a dictionary: for (const auto& kv : p.asMap()) { const std::string key = kv.first.as<std::string>(); const auto& valueNode = kv.second; std::cout << "`" << key << "`: " << mrpt::demangle(valueNode.typeName()) << "\n"; } // Iterate a dictionary bis: mrpt::containers::yaml p2; p2["a"] = 1.0; p2["b"] = 10; p2["c"] = -1; for (const auto& kv : p2.asMap()) { // This will raise an exception if stored type cannot be converted to // double: std::cout << "key: `" << kv.first << "` val: " << kv.second.as<double>() << "\n"; } // Iterate sequence: for (const auto& item : p["books"].asSequence()) { std::cout << "sequence item type: " << mrpt::demangle(item.typeName()) << "\n"; // Access value: kv.second.as<std::string>(), etc. } // Print: std::cout << "\n\nPrint as YAML:\n"; p.printAsYAML(); } void YamlTest_2() { std::cout << "\n\n==== YamlTest_2 ====\n"; // You can use {} to initialize mappings (dictionaries): using mrpt::containers::CommentPosition; using mrpt::containers::vcp; using mrpt::containers::vkcp; mrpt::containers::yaml p; // Insert a key in a map with a "comment" block. p << vkcp("L", 5.5, "Arm length [meters]") << vkcp("D", 1.0, "Distance [meters]") << vkcp("Y", -5, "Comment for Y"); ASSERT_(p.isMap()); ASSERT_(p.has("L")); ASSERT_(p["L"].isScalar()); ASSERT_(p.keyHasComment("D")); // Add comment associated to value (not key): p["X"] = vcp(1.0, "Default value"); std::cout << "D key comment: " << p.keyComment("D") << "\n"; std::cout << "X value comment: " << p["X"].comment() << "\n"; // Print: std::cout << "\n\nPrint as YAML:\n"; p.printAsYAML(std::cout); } const auto sData = std::string(R"xxx( myMap: K: 10.0 P: -5.0 Q: ~ nestedMap: a: 1 # comment for a b: 2 c: 3 )xxx"); void YamlTest_3() { std::cout << "\n\n==== YamlTest_3 ====\n"; // Parse a YAML or JSON text: mrpt::containers::yaml p = mrpt::containers::yaml::FromText(sData); // Get comments: std::cout << "Comment: '" << p["myMap"]["nestedMap"]["a"].comment() << "'\n"; // Manipulate comments: p["myMap"]["nestedMap"]["b"].comment("This is a comment for b"); // Add values and comments at once: p["myMap"]["foo"] = mrpt::containers::vcp(1.0, "Another constant"); std::cout << "\n\nPrint as YAML:\n"; p.printAsYAML(std::cout); }
Output:
==== YamlTest_1 ==== K=2 N=10 name=Foo Movie year=1986 bar=none `K`: scalar(double) `N`: scalar(int) `books`: sequence `enabled`: scalar(bool) `movies`: sequence `name`: scalar(std::string) key: `a` val: 1 key: `b` val: 10 key: `c` val: -1 sequence item type: scalar(bool) sequence item type: scalar(double) Print as YAML: %YAML 1.2 --- K: 2 N: 10 books: - true - 10 enabled: true movies: - title: xxx year: 2001 - title: yyy year: 1986 name: Foo ==== YamlTest_2 ==== D key comment: Distance [meters] X value comment: Default value Print as YAML: %YAML 1.2 --- # Distance [meters] D: 1 # Arm length [meters] L: 5.5 X: 1 # Default value # Comment for Y Y: -5 ==== YamlTest_3 ==== Comment: 'comment for a' Print as YAML: %YAML 1.2 --- myMap: K: 10.0 P: -5.0 Q: ~ foo: 1 # Another constant nestedMap: a: 1 # comment for a b: 2 # This is a comment for b c: 3
Verbose debug information on YAML document parsing is emitted if the environment variable MRPT_YAML_PARSER_VERBOSE
is set to 1
.
[New in MRPT 2.1.0]
#include <mrpt/containers/yaml.h> class yaml { public: // typedefs typedef std::any scalar_t; typedef std::vector<node_t> sequence_t; typedef std::map<node_t, node_t> map_t; typedef std::array<std::optional<std::string>, static_cast<size_t>(CommentPosition::MAX)> comments_t; // structs struct InternalPrintState; struct node_t; // construction yaml(); yaml(std::initializer_list<map_t::value_type> init); yaml(std::initializer_list<sequence_t::value_type> init); yaml(const yaml& v); yaml(const node_t& s); // methods void loadFromText(const std::string& yamlTextBlock); void loadFromFile(const std::string& fileName); void loadFromStream(std::istream& i); template <typename YAML_NODE> void loadFromYAMLCPP(const YAML_NODE& n); template <typename MATRIX> void toMatrix(MATRIX& m) const; template <typename Scalar> std::vector<Scalar> toStdVector() const; static node_t Sequence(std::initializer_list<sequence_t::value_type> init); static node_t Sequence(); static node_t Map(std::initializer_list<map_t::value_type> init); static node_t Map(); static yaml FromText(const std::string& yamlTextBlock); static yaml FromStream(std::istream& i); static yaml FromFile(const std::string& fileName); template <typename YAML_NODE> static yaml FromYAMLCPP(const YAML_NODE& n); template <typename MATRIX> static yaml FromMatrix(const MATRIX& m); bool has(const std::string& key) const; bool empty() const; void clear(); bool isNullNode() const; bool isScalar() const; bool isSequence() const; bool isMap() const; const std::type_info& scalarType() const; sequence_t& asSequence(); const sequence_t& asSequence() const; map_t& asMap(); const map_t& asMap() const; scalar_t& asScalar(); const scalar_t& asScalar() const; size_t size() const; node_t& node(); const node_t& node() const; const node_t& keyNode(const std::string& keyName) const; node_t& keyNode(const std::string& keyName); void printAsYAML(std::ostream& o, const YamlEmitOptions& eo = {}) const; void printAsYAML() const; void printDebugStructure(std::ostream& o) const; yaml operator [] (const std::string& key); yaml operator [] (const char* key); const yaml operator [] (const std::string& key) const; const yaml operator [] (const char* key) const; template <typename T> const T getOrDefault(const std::string& key, const T& defaultValue) const; template <typename T> T as() const; template <typename T> T& asRef(); template <typename T> const T& asRef() const; yaml& operator = (bool v); yaml& operator = (float v); yaml& operator = (double v); yaml& operator = (int8_t v); yaml& operator = (uint8_t v); yaml& operator = (int16_t v); yaml& operator = (uint16_t v); yaml& operator = (int32_t v); yaml& operator = (uint32_t v); yaml& operator = (int64_t v); yaml& operator = (uint64_t v); template <typename = std::enable_if<!std::is_same_v<std::size_t, uint64_t>&& !std::is_same_v<std::size_t, int64_t>&& !std::is_same_v<std::size_t, uint32_t>&& !std::is_same_v<std::size_t, int32_t>>> yaml& operator = (std::size_t v); yaml& operator = (const std::string& v); yaml& operator = (const char* v); yaml& operator = (const std::string_view& v); yaml& operator = (const yaml& v); template <typename T> yaml& operator = (const ValueCommentPair<T>& vc); template <typename T> yaml& operator << (const ValueKeyCommentPair<T>& vc); operator bool () const; operator double () const; operator float () const; operator int8_t () const; operator uint8_t () const; operator int16_t () const; operator uint16_t () const; operator int32_t () const; operator uint32_t () const; operator int64_t () const; operator uint64_t () const; operator std::string () const; bool hasComment() const; bool hasComment(CommentPosition pos) const; const std::string& comment() const; const std::string& comment(CommentPosition pos) const; void comment(const std::string& c, CommentPosition position = CommentPosition::RIGHT); bool keyHasComment(const std::string& key) const; bool keyHasComment(const std::string& key, CommentPosition pos) const; const std::string& keyComment(const std::string& key) const; const std::string& keyComment(const std::string& key, CommentPosition pos) const; void keyComment(const std::string& key, const std::string& c, CommentPosition position = CommentPosition::TOP); yaml operator () (int index); const yaml operator () (int index) const; void push_back(const double v); void push_back(const std::string v); void push_back(const uint64_t v); void push_back(const bool v); void push_back(const yaml& v); };
Construction
yaml(std::initializer_list<map_t::value_type> init)
Constructor for maps, from list of pairs of values.
See examples in yaml above.
yaml(std::initializer_list<sequence_t::value_type> init)
Constructor for sequences, from list of values.
See examples in yaml above.
Methods
void loadFromText(const std::string& yamlTextBlock)
Parses a text as YAML or JSON (autodetected) and stores the contents into this document.
Parameters:
std::exception |
Upon format errors |
void loadFromFile(const std::string& fileName)
Parses a text as YAML or JSON (autodetected) and stores the contents into this document.
Parameters:
std::exception |
Upon I/O or format errors |
void loadFromStream(std::istream& i)
Parses the stream as YAML or JSON (autodetected) and stores the contents into this document.
Parameters:
std::exception |
Upon format errors |
template <typename YAML_NODE> void loadFromYAMLCPP(const YAML_NODE& n)
This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.
template <typename MATRIX> void toMatrix(MATRIX& m) const
Fills in a matrix from a yaml dictionary node.
The matrix can be either an Eigen or mrpt::math matrix. Example yaml node (compatible with OpenCV & ROS YAML formats):
rows: 2 cols: 3 data: [11, 12, 13, 21, 22, 23]
See also:
template <typename Scalar> std::vector<Scalar> toStdVector() const
Converts a sequence yaml node into a std::vector, trying to convert all nodes to the same given Scalar
type.
(New in MRPT 2.3.3)
static yaml FromText(const std::string& yamlTextBlock)
Parses a text as YAML or JSON (autodetected) and returns a document.
Parameters:
std::exception |
Upon format errors |
static yaml FromStream(std::istream& i)
Parses the stream as YAML or JSON (autodetected) and returns a document.
Parameters:
std::exception |
Upon format errors |
static yaml FromFile(const std::string& fileName)
Parses the filename as YAML or JSON (autodetected) and returns a document.
Parameters:
std::exception |
Upon I/O or format errors. |
template <typename YAML_NODE> static yaml FromYAMLCPP(const YAML_NODE& n)
Builds an object copying the structure and contents from an existing YAMLCPP Node.
Requires user to #include yamlcpp from your calling program (does NOT requires yamlcpp while compiling mrpt itself).
Parameters:
YAML_NODE |
Must be |
template <typename MATRIX> static yaml FromMatrix(const MATRIX& m)
Creates a yaml dictionary node from an Eigen or mrpt::math matrix.
Example (compatible with OpenCV & ROS YAML formats):
rows: 2 cols: 3 data: [11, 12, 13, 21, 22, 23]
See also:
bool has(const std::string& key) const
For map nodes, checks if the given key name exists.
Returns false if the node is a null
node. Throws if the node is not a map or null.
bool empty() const
For map or sequence nodes, checks if the container is empty.
Also returns true for null(empty) nodes.
void clear()
Resets to empty (can be called on a root node or any other node to clear that subtree only).
const std::type_info& scalarType() const
For scalar nodes, returns its type, or typeid(void) if an empty node.
Parameters:
std::exception |
If called on a map or sequence. |
sequence_t& asSequence()
Use: for (auto &kv: n.asSequence()) {...}
Parameters:
std::exception |
If called on a non-sequence node. |
map_t& asMap()
Use: for (auto &kv: n.asMap()) {...}
Parameters:
std::exception |
If called on a non-map node. |
scalar_t& asScalar()
Parameters:
std::exception |
If called on a non-scalar node. |
size_t size() const
Returns 1 for null or scalar nodes, the number of children for sequence or map nodes.
node_t& node()
For a master yaml document, returns the root node; otherwise, the referenced node.
const node_t& node() const
This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.
const node_t& keyNode(const std::string& keyName) const
Maps only: returns a reference to the key node of a key-value pair.
Parameters:
std::exception |
If called on a non-map node or key does not exist. |
void printAsYAML(std::ostream& o, const YamlEmitOptions& eo = {}) const
Prints the document in YAML format to the given stream.
void printAsYAML() const
This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.
void printDebugStructure(std::ostream& o) const
Prints a tree-like representation of all nodes in the document in a custom format (nor YAML neither JSON).
yaml operator [] (const std::string& key)
Write access for maps.
yaml operator [] (const char* key)
This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.
const yaml operator [] (const std::string& key) const
Read access for maps.
Parameters:
std::runtime_error |
if key does not exist. |
const yaml operator [] (const char* key) const
This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.
template <typename T> const T getOrDefault( const std::string& key, const T& defaultValue ) const
Scalar read access for maps, with default value if key does not exist.
template <typename T> T as() const
Returns a copy of the existing value of the given type, or tries to convert it between easily-compatible types (e.g.
double<->int, string<->int).
Parameters:
std::exception |
If the contained type does not match and there is no obvious conversion. |
template <typename T> T& asRef()
Returns a ref to the existing or new value of the given type.
If types do not match, the old content will be discarded and a new variable created into this scalar node.
Parameters:
std::exception |
If accessing to a non-scalar node. |
template <typename T> const T& asRef() const
const version of asRef().
Unlike as<T>()
, this version will NOT try to convert between types if T does not match exactly the stored type, and will raise an exception instead.
template <typename T> yaml& operator = (const ValueCommentPair<T>& vc)
vcp (value-comment) wrapper
template <typename T> yaml& operator << (const ValueKeyCommentPair<T>& vc)
vkcp (value-keyComment) wrapper
bool hasComment() const
Returns true if the proxied node has an associated comment block, at any location.
bool hasComment(CommentPosition pos) const
Returns true if the proxied node has an associated comment block at a particular position.
const std::string& comment() const
Gets the comment associated to the proxied node.
This version returns the first comment, of all possible (top, right).
Parameters:
std::exception |
If there is no comment attached. |
See also:
const std::string& comment(CommentPosition pos) const
Gets the comment associated to the proxied node, at the particular position.
See code examples in mrpt::containers::yaml.
Parameters:
std::exception |
If there is no comment attached. |
See also:
void comment(const std::string& c, CommentPosition position = CommentPosition::RIGHT)
Sets the comment attached to a given proxied node.
See code examples in mrpt::containers::yaml
See also:
bool keyHasComment(const std::string& key) const
Maps only: returns true if the given key node has an associated comment block, at any location.
Parameters:
std::exception |
If called on a non-map or key does not exist. |
bool keyHasComment(const std::string& key, CommentPosition pos) const
Maps only: Returns true if the given key has an associated comment block at a particular position.
Parameters:
std::exception |
If called on a non-map or key does not exist. |
const std::string& keyComment(const std::string& key) const
Maps only: Gets the comment associated to the given key.
This version returns the first comment, of all possible (top, right).
Parameters:
std::exception |
If called on a non-map or key does not exist. |
std::exception |
If there is no comment attached. |
See also:
const std::string& keyComment(const std::string& key, CommentPosition pos) const
Maps only: Gets the comment associated to the given key, at the particular position.
See code examples in mrpt::containers::yaml.
Parameters:
std::exception |
If called on a non-map or key does not exist. |
std::exception |
If there is no comment attached. |
See also:
void keyComment(const std::string& key, const std::string& c, CommentPosition position = CommentPosition::TOP)
Maps only: Sets the comment attached to a given key.
See code examples in mrpt::containers::yaml
Parameters:
std::exception |
If called on a non-map or key does not exist. |
See also:
yaml operator () (int index)
Write into an existing index of a sequence.
Parameters:
std::out_of_range |
if index is out of range. |
const yaml operator () (int index) const
Read from an existing index of a sequence.
Parameters:
std::out_of_range |
if index is out of range. |
void push_back(const double v)
Append a new value to a sequence.
Parameters:
std::exception |
If this is not a sequence |
void push_back(const std::string v)
This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.
void push_back(const uint64_t v)
This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.
void push_back(const bool v)
This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.
void push_back(const yaml& v)
This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.