user3293204
user3293204

Reputation: 161

Yaml-cpp (new API): Problems mixing maps and scalars in a sequence

I have a very simple problem parsing a yaml file of this form:

- Foo
- Bar:
   b1: 5

I would like to parse the top level keys as strings namely "Foo" and "Bar". As you can see the first entry in the sequence is a scalar and the second is a map containing one key/value pair. Let's say I've loaded this YAML text into a node called config. I iterate over config in the following way:

YAML::Node::const_iterator n_it = config.begin();
for (; n_it != config.end(); n_it++) {
    std::string name;
    if (n_it->Type() == YAML::NodeType::Scalar)
        name = n_it->as<std::string>();
    else if (n_it->Type() == YAML::NodeType::Map) {
        name = n_it->first.as<std::string>();
    }
}

The problem is parsing the second "Bar" entry. I get the following yaml-cpp exception telling me I'm trying to access the key from a sequence iterator n_it.

YAML::InvalidNode: yaml-cpp: error at line 0, column 0: invalid node; this may result from using a map iterator as a sequence iterator, or vice-versa

If I change the access to this:

name = n_it->as<std::string>();

I get a different yaml-cpp exception which I guess is due to the fact that I'm trying to access the whole map as a std::string

YAML::TypedBadConversion<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >: yaml-cpp: error at line 0, column 0: bad conversion

Can somebody please explain to me what's going wrong?

Edit: new problems I'm still having problems with this api's handling of maps vs sequences. Now say I have the following structure:

foo_map["f1"] = "one";
foo_map["f2"] = "two";
bar_map["b1"] = "one";
bar_map["b2"] = "two";

I want this to be converted to the following YAML file:

Node: 
    - Foo:
      f1 : one
      f2 : two
    - Bar:
      b1 : one
      b2 : two

I would do so by doing:

node.push_back("Foo");
node["Foo"]["b1"] = "one";
...
node.push_back("Bar");

However at the last line node has now been converted from a sequence to a map and I get an exception. The only way I can do this is by outputting a map of maps:

Node:
    Foo:
      f1 : one
      f2 : two
    Bar:
      b1 : one
      b2 : two

The problem with this is if I cannot read back such files. If I iterate over Node, I'm unable to even get the type of the node iterator without getting an exception.

YAML::Node::const_iterator n_it = node.begin();

for (; n_it != config.end(); n_it++) {
        if (n_it->Type() == YAML::NodeType::Scalar) {
            // throws exception
        }
    }

This should be very simple to handle but has been driving me crazy!

Upvotes: 5

Views: 11964

Answers (4)

Audrius Meškauskas
Audrius Meškauskas

Reputation: 21778

This can also be done with the new C++ loop:

      std::string name;
      for (const auto &entry: node_x) {
        assert(name.empty());
        name = entry.first.as<std::string>();
      }

The assertion will trigger if the node_x is something else than you think. It should be only one entry in this map.

Upvotes: 0

Nitin Ashutosh
Nitin Ashutosh

Reputation: 131

Sample.yaml

config:
  key1: "SCALER_VAL" # SCALER ITEM
  key2: ["val1", "val2"] #SEQUENCE ITEM
  key3: # MAP ITEM
    nested_key1: "nested_val"


#SAMPLE CODE for Iterate Yaml Node;
YAML::Node internalconfig_yaml = YAML::LoadFile(configFileName);
const YAML::Node &node = internalconfig_yaml["config"];
for(const auto& it : node )
{
    std::cout << "\nnested Key: " << it.first.as<std::string>() << "\n";
    if (it.second.Type() == YAML::NodeType::Scalar)
    {
        std::cout << "\nnested value: " << std::to_string(it.second.as<int>()) << "\n";
    }
    if (it.second.Type() == YAML::NodeType::Sequence)
    {
        std::vector<std::string> temp_vect;
        const YAML::Node &nestd_node2 = it.second;
        for(const auto& it2 : nestd_node2)
        {
            if (*it2)
            {
                std::cout << "\nnested sequence value: " << it2.as<std::string>() << "\n";
                temp_vect.push_back(it2.as<std::string>());
             }
        }
        std::ostringstream oss;
        std::copy(temp_vect.begin(), temp_vect.end(),                 
                  std::ostream_iterator<std::string>(oss, ","));
        std::cout << "\nnested sequence as string: " <<oss.str() << "\n";
    }
    if (it2.second.Type() == YAML::NodeType::Map)
    {
    // Iterate Recursively again !!
    }
}

Refer here for more details;

Upvotes: 0

Jesse Beder
Jesse Beder

Reputation: 34054

In your expression

name = n_it->first.as<std::string>();

n_it is a sequence iterator (since it's an iterator for your top-level node), which you've just established points to a map. That is,

YAML::Node n = *n_it;

is a map node. This map node (in your example) looks like:

Bar:
  b1: 5

In other words, it has a single key/value pair, with the key a string, and the value a map node. It sounds like you want the string key. So:

assert(n.size() == 1);  // Verify that there is, in fact, only one key/value pair
YAML::Node::const_iterator sub_it = n.begin();  // This iterator points to
                                                // the single key/value pair
name = sub_it->first.as<std::string>();

Upvotes: 5

jsuth
jsuth

Reputation: 201

Try something like this:

- Foo: {}
- Bar:
  b1: 15

Upvotes: -1

Related Questions