RapidXML attribute value errors

816 Views Asked by At

I have used RapidXML in the past with few problems but this one has me stumped.

I am creating a log of application event timestamps which an external program can read in an replay any audio which occured in the origional application at the correct times.

On initialisation of the application, the following XML is correctly generated:

<?xml version="1.0" encoding="utf-8"?>
<playbacklog>
    <logitem type="general" event="start" timestamp="85639323"/>
</playbacklog>

Once the next item is added, the documents then becomes this:

<?xml version="1.0" encoding="utf-8"?>
<playbacklog>
    <logitem type="general" event="start" timestamp="NUL NUL NUL NUL"/>
    <logitem type="audio" event="start" timestamp="86473833">
</playbacklog>

and then:

<?xml version="1.0" encoding="utf-8"?>
<playbacklog>
    <logitem type="general" event="start" timestamp="@NUL NUL' NUL NUL"/>
    <logitem type="audio" event="start" timestamp="NUL NUL NUL NUL">
    <logitem type="audio" event="stop" timestamp="8654533">
</playbacklog>

With the addition of each new start and stop pair, the final following behaviour is also seen, with the timestamp value being changed for all of the nodes with the same event attribute value:

<?xml version="1.0" encoding="utf-8"?>
<playbacklog>
    <logitem type="general" event="start" timestamp="@NUL NUL' NUL NUL"/>
    <logitem type="audio" event="start" timestamp="NUL NUL NUL NUL">
    <logitem type="audio" event="stop" timestamp="8674519">
    <logitem type="audio" event="start" timestamp="NUL NUL NUL NUL">
    <logitem type="audio" event="stop" timestamp="8674519">
    <logitem type="audio" event="start" timestamp="NUL NUL NUL NUL">
    <logitem type="audio" event="stop" timestamp="8674519">
</playbacklog>

I declare the document as such in the c++ header file:

private:
    rapidxml::xml_document<> outputDocument;

In order to create each node, I am using the following code:

// tStamp is a typedef'd std::pair containing two std::string values, one for the
// time at which the evet occurred and the other containing the event type.
void AudioLogger::LogEvent( Timestamp* tStamp )
{
    rapidxml::xml_node<>* nodeToAdd = outputDocument.allocate_node(rapidxml::node_element, "logitem");
    ...
    nodeToAdd->append_attirbute(outputDocument.allocate_attribute("timestamp", ts->first.c_str()));
    ...

    outputDocument.first_node()->next_sibling()->append_node(nodeToAdd);
}

The TimeStamp* values passed to this function as held in a std::vector and when a new one is added, this function is called.

If anyone has any ideas on what is happening here, it would be a massive help. Also, if more information is required, I will also be able to provide this.

1

There are 1 best solutions below

0
On

This is a classic RapidXML 'gotcha'. Whenever you pass a char pointer to RapidXML , it simply stores the pointer rather than making a copy of the string. It's clearly documented, but still frequently catches people out. http://rapidxml.sourceforge.net/manual.html#namespacerapidxml_1modifying_dom_tree

The answer is to use the allocate_string function like this:

 nodeToAdd->append_attribute(outputDocument.allocate_attribute("timestamp", 
                             outputDocument.allocate_string(ts->first.c_str()))); 

(You don't need to wrap "timestamp" in allocate_string, because that's a literal and so wont change).

I usually use my own helper wrapper - something like this:-

class MyRapidXmlDoc : public rapidxml::xml_document<char>
{
...
  Attribute* allocateAttribute(const string &name, const string &value = "")
  {
    if (value.empty())
      return allocate_attribute(allocate_string(name.c_str()));
    else
      return allocate_attribute(allocate_string(name.c_str()), allocate_string(value.c_str()));
  }