xmlboostboost-propertytreechild-nodes

boost property_tree: iterating through attributes of repeated child elements within repeated child elements


I have the following XML document:

<root>
  <scenario name="ScenarioA">
    <param name="ParamA">1</param>
    <param name="ParamB">2</param>
    <param name="ParamC">3</param>
  </scenario>
  <scenario name="ScenarioB">
    <param name="ParamA">1</param>
    <param name="ParamB">2</param>
    <param name="ParamC">3</param>
  </scenario>
  <scenario name="ScenarioC">
    <param name="ParamA">1</param>
    <param name="ParamB">2</param>
    <param name="ParamC">3</param>
  </scenario>
</root>

Using boost::property_tree::ptree, I can do the following to iterate through the top-level elements:

ptree scenarioTree = myTree.get_child("root");
for (const auto& itr : scenarioTree) {
    if (itr.second.get<std::string>(<xmlattr>.name) == "ScenarioA") {
        // additional logic
    }
}

I am unable to retrieve any attributes of the "param" tags under each scenario, using the same method on itr.second:

ptree scenarioTree = myTree.get_child("root");
for (const auto& itr : scenarioTree) {
    if (itr.second.get<std::string>(<xmlattr>.name) == "ScenarioA") {
        ptree paramTree = itr.second;
        // Iterate through "param" tags
        for (const auto& paramItr: paramTree) {
            if (paramItr.second.get<std::string>(<xmlattr>.name) == "ParamA") {
                // doesn't exist
            }
        }
    }
}

What am I doing wrong? My theory is this a casting issue between what the ptree iterator returns (self) versus a standard ptree, but I'm not sure how else to retrieve the inner child attributes. I am able to retrieve param tag names (via first) and param values (via second.data()), just no attributes.


Solution

  • Attributes in property tree are added as sub-nodes. Below node can be translated

      <scenario name="ScenarioA">
        <param name="ParamA">1</param>
        <param name="ParamB">2</param>
        <param name="ParamC">3</param>
      </scenario>
    

    into something like this:

      scenario 
         xmlattr.name   ScenarioA // this node doesn't have subnode with attribute name
         param 
           xmlattr.name  -> ParamA
         param 
           xmlattr.name  -> ParamB
         param 
           xmlattr.name  -> ParamC
    

    When you found scenario with ScenarioA as attribute you are iterating over its children. This node has 4 children. For each child you want to find name attribute, but one of them doesn't have this attribute. Method get throws exception when path cannot be found. You can catch this exception or use default-value:

    boost::property_tree::ptree scenarioTree = pt.get_child("root");
    for (const auto& itr : scenarioTree) 
    {
        if (itr.second.get<std::string>("<xmlattr>.name") == "ScenarioA") 
        {
            boost::property_tree::ptree paramTree = itr.second;
            for (const auto& paramItr: paramTree) 
            {
                if (paramItr.second.get<std::string>("<xmlattr>.name","") == "ParamA") 
                {                                                    ^^^^ default value
                    cout << "ParamA was found in ScenarioA" << endl;
                }
            }
        }
    }
    

    as result I got ParamA was found in ScenarioA.