yaml-cpp
yaml-cpp copied to clipboard
How to access sub-nodes
yaml-cpp: 0.6.2 Arch: Linux x86_64 Compiler: gcc/7.2.0, C++14
I am trying to write a general purpose access routine to pull the line number from a tree. I have
class YamlParser {
YAML::Node m_node;
...
}
int YamlParser::getLineNumber(std::vector<std::string> keywords) const
{
YAML::Node findNode = m_node;
for(std::size_t i=0; i<keywords.size(); i++) {
std::string keyword = keywords.at(i);
std::string strNum = "";
for(std::size_t j=0; j<keyword.length(); j++) {
if(std::isdigit(keyword.at(j))) {
strNum += keyword.at(j);
}
}
if(keyword == strNum) {
std::cout << "int '" << std::stoi(keyword) << "'" << std::endl;
findNode = findNode[std::stoi(keyword)];
} else {
findNode = findNode[keyword];
}
}
YAML::Mark mark = findNode.Mark();
return mark.line;
}
The input file (yes, it is JSON, but I use yaml-cpp to generate it from a YAML file - awesome) is:
{
"VALUE1": 1,
"VALUEA": "A",
"ARRAY1": [
{ "A123": [ 1, 2, 3 ] },
{ "A456": [ 4, 5, 6 ] },
{ "A789": [ 7, 8, 9 ] } ],
"VALUEXYZ": {
"VALUEX": {
"X1": 1,
"X2": 2,
"X3": 3 },
"VALUEY": "Y" }
}
i = yamlTree.getLineNumber({"VALUEXYZ", "VALUEX", "X1"}); works great, returning '9' before I changed to Clone(m_node), then they were zero, but I also need to access the nodes in ARRAY1 via: i = yamlTree.getLineNumber({"ARRAY1", "2"}); where 2 is converted to an integer via findNode = findNode[std::stoi(keyword)]; but the line numbers is 0 Accessing directly is not an option and it needs to be in a function. Any ideas? Thanks in advanced.
I'm not sure I understand, but is this a bug? If not, questions regarding how to use yaml-cpp should be asked on SO: "If you have questions about how to use yaml-cpp, please post it on http://stackoverflow.com and tag it yaml-cpp.". Try to make a self-contained minimal example. This could be something to that you could use as a base and extended with what you're trying to do, the expected result and what's really happening:
#include <yaml-cpp/yaml.h>
#include <cctype>
#include <exception>
#include <iostream>
#include <string>
#include <vector>
int getLineNumber(const std::vector<std::string>& keywords,
const YAML::Node& m_node) {
YAML::Node findNode = m_node;
for(const auto& keyword : keywords) {
std::string strNum = "";
for(char ch : keyword) {
if(std::isdigit(ch)) {
strNum += ch;
}
}
if(keyword == strNum) {
std::cout << "int '" << std::stoi(keyword) << "'" << std::endl;
findNode = findNode[std::stoi(keyword)];
} else {
findNode = findNode[keyword];
}
}
YAML::Mark mark = findNode.Mark();
return mark.line;
}
int main() {
try {
YAML::Node m_node = YAML::Load(R"yaml({
"VALUE1": 1,
"VALUEA": "A",
"ARRAY1": [
{ "A123": [ 1, 2, 3 ] },
{ "A456": [ 4, 5, 6 ] },
{ "A789": [ 7, 8, 9 ] } ],
"VALUEXYZ": {
"VALUEX": {
"X1": 1,
"X2": 2,
"X3": 3 },
"VALUEY": "Y" }
})yaml");
// This works and prints 9 :
std::cout << getLineNumber({"VALUEXYZ", "VALUEX", "X1"}, m_node) << '\n';
} catch(std::exception& ex) {
std::cout << ex.what() << '\n';
}
}
Btw, I think you'll get the expected result if you start the function with
YAML::Node findNode = YAML::Clone(m_node);
I apologize for the placement of a question here I tried Clone and discovered that Clone does not copy over the Mark information. I submitted a bug for that, but I will update it with your example slightly modified.
I see what you mean. I made this small example to show the diff betweeing Clone()ing vs. using the copy constructor.
#include <yaml-cpp/yaml.h>
#include <cctype>
#include <charconv>
#include <exception>
#include <iostream>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
auto is_pure_unsigned_integer(const std::string_view sv) {
unsigned long long res;
auto [ptr, ec] = std::from_chars(sv.cbegin(), sv.cend(), res);
return std::pair{ec != std::errc::invalid_argument && ptr == sv.cend(), res};
}
int getLineNumber(const std::vector<std::string>& keywords, const YAML::Node& m_node,
bool clone)
{
YAML::Node findNode = clone ? YAML::Clone(m_node) : m_node;
for(const auto& keyword : keywords) {
auto [is_uint, value] = is_pure_unsigned_integer(keyword);
std::cout << "before '" << keyword << "': " << findNode
<< " Mark().line: " << findNode.Mark().line << '\n';
if(is_uint) {
std::cout << "int '" << value << "'\n";
findNode = findNode[value];
} else {
findNode = findNode[keyword];
}
std::cout << "after '" << keyword << "': " << findNode
<< " Mark().line: " << findNode.Mark().line << '\n';
}
return findNode.Mark().line;
}
int main() {
try {
YAML::Node m_node = YAML::Load(R"yaml({
"VALUE1": 1,
"VALUEA": "A",
"ARRAY1": [
{ "A123": [ 1, 2, 3 ] },
{ "A456": [ 4, 5, 6 ] },
{ "A789": [ 7, 8, 9 ] } ],
"VALUEXYZ": {
"VALUEX": {
"X1": 1,
"X2": 2,
"X3": 3 },
"VALUEY": "Y" }
})yaml");
std::cout << "Cloning test:\n";
std::cout << getLineNumber({"ARRAY1", "2"}, m_node, true) << '\n';
std::cout << "---\n\n";
std::cout << getLineNumber({"ARRAY1", "2"}, m_node, true) << '\n';
std::cout << "---\n\n";
std::cout << "\nCopy test:\n";
std::cout << getLineNumber({"ARRAY1", "2"}, m_node, false) << '\n';
std::cout << "---\n\n";
std::cout << getLineNumber({"ARRAY1", "2"}, m_node, false) << '\n';
std::cout << "---\n\n";
} catch(std::exception& ex) {
std::cout << ex.what() << '\n';
}
}
The returned values are:
Cloning test:
0
0
Copy test:
6
-1
Ah... sorry for the code spam. I see you already put up an example, but perhaps the above shows it even more clearly.
oh, I want to access the sub nodes in a node, but I don't actually know their names. What shall I do to get the sub nodes' keys.