yaml-cpp icon indicating copy to clipboard operation
yaml-cpp copied to clipboard

How to access sub-nodes

Open mjcrawford22 opened this issue 5 years ago • 6 comments

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.

mjcrawford22 avatar Jan 29 '20 21:01 mjcrawford22

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';
    }
}

TedLyngmo avatar Jan 30 '20 07:01 TedLyngmo

Btw, I think you'll get the expected result if you start the function with
YAML::Node findNode = YAML::Clone(m_node);

TedLyngmo avatar Jan 30 '20 13:01 TedLyngmo

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.

mjcrawford22 avatar Jan 30 '20 13:01 mjcrawford22

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

TedLyngmo avatar Jan 30 '20 15:01 TedLyngmo

Ah... sorry for the code spam. I see you already put up an example, but perhaps the above shows it even more clearly.

TedLyngmo avatar Jan 30 '20 15:01 TedLyngmo

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.

wudifei1991 avatar Jul 14 '22 09:07 wudifei1991