2

I am using rapidXML and C++ in VS2012 on PC. I have already parsed the XML file, but now I want to print out attribute values individually. I am usually able to do this using the code below. This method, however, needs to know the node name and attribute name. This is a problem because I have multiple nodes with the same name and multiple attributes with the same name. My question is this, how do I get a single attribute value when neither the node name, nor the attribute name, is unique?

The code I use when I have a unique node name and attribute name:

xml_node<> *node0 = doc.first_node("NodeName"); //define the individual node you want to access
xml_attribute<> *attr = node0->first_attribute("price"); //define the individual attribute that you want to access
cout << "Node NodeName has attribute " << attr->name() << " ";
cout << "with value " << attr->value() << "\n";

My XML test file:

<catalog>
  <book>
      <author>Gambardella, Matthew</author>
      <title>XML Developer's Guide</title>
      <price>44.95</price>
  </book>
  <book>
  <author>Ralls, Kim</author>
  <title>Midnight Rain</title>
  <price>5.95</price>
  </book>
</catalog>

For this specific example, how can I get the value of the price attribute on the second book? Can I enter the title attribute value "Midnight Rain" and somehow use that to get the next value?

Ty Roderick
  • 41
  • 1
  • 3

2 Answers2

1

You can use the next_sibling(const char *) member function to iterate over sibling nodes until you find one with the right attribute value. I haven't tested the following code, but it should give an idea of what you need to do:

typedef rapidxml::xml_node<>      node_type;
typedef rapidxml::xml_attribute<> attribute_type;

/// find a child of a specific type for which the given attribute has 
/// the given value...
node_type *find_child( 
    node_type *parent, 
    const std::string &type, 
    const std::string &attribute, 
    const std::string &value)
{
    node_type *node = parent->first_node( type.c_str());
    while (node)
    {
        attribute_type *attr = node->first_attribute( attribute.c_str());
        if ( attr && value == attr->value()) return node;
        node = node->next_sibling( type.c_str());
    }
    return node;
}

You could then find the second book by calling:

node_type *midnight = find_child( doc, "book", "title", "Midnight Rain");

Getting the price of that book should then be easy.

In general, when dealing with rapidxml, I tend to create many of such little helper functions. I find they make my code easier to read in the absence of xpath functions...

dhavenith
  • 1,958
  • 11
  • 13
  • Your code isn't running because node_type is an "ambiguous symbol" ... I also don't fully understand what node_type trying to do. Do you think you could comment your code a little more, or possibly even test it yourself? I apologize if this problem seems obvious, this is my first time working with rapidxml. – Ty Roderick Jul 16 '13 at 20:20
  • apparently, in your environment you already have a symbol 'node_type'. You could replace node_type in the code above with xml_node<>. Note that it is only an illustration. The key message is: use `next_sibling(const char *)` as in the example above. – dhavenith Jul 16 '13 at 21:40
0

When you say multiple nodes with the same name and multiple attributes with the same name, do you mean; multiple node and multiple , , attributes? If that is the case then I think you are trying to pass multiple xml messages. However you should be able to pass the first xml message first then the second message successfully. You did not include the entire code, I would check first if doc.first_node method actually creates an xml_node.

Juniar
  • 863
  • 1
  • 10
  • 22