Stav
Stav

Reputation: 11

TinyXML and problem with tags and formatting

I have a problem with TinyXML that can't be able to fix. I have extended an MFC application using C++ for work. The application does automated testing. After it completes it saves all the data in an XML file. Below the beginning of my DOM tree.

<?xml version="1.0" encoding="utf-8" ?>
<TestData>
<Operator>Alex</Operator>
<ParentBarCode>12345
</ParentBarCode>
<Measurements>

The issue is the library seems to be adding a random newline after 12345. It should be:

<ParentBarCode>12345</ParentBarCode>

Could you help me please? I have tried everything... Code snippet below. Also, is there any way to remove a newline before closing the tag of a new element , i.e. before

</ParentBarCode>?? 
//Code starts here
TiXmlDocument doc;
TiXmlDeclaration * decl = new TiXmlDeclaration("1.0","utf-8", "");
//doc.FirstChildElement();
doc.LinkEndChild(decl);
TiXmlElement *rootelement = new TiXmlElement("TestData");
doc.LinkEndChild(rootelement);
TiXmlText *textTestData = new TiXmlText("");
rootelement->LinkEndChild(textTestData);
//Operator node
TiXmlElement *Operator = new TiXmlElement("Operator"); 
//Tree root
rootelement->LinkEndChild(Operator);
TiXmlText *textOperator = new TiXmlText("Alex");
Operator->LinkEndChild(textOperator);
//ParentBarcode Node
TiXmlElement *barcode = new TiXmlElement("ParentBarCode"); 
//Tree root
rootelement->LinkEndChild(barcode);

//saving serial to a stringstream

stringstream serial;
serial << (DlgPtr->m_lSerialNumber);
std::string myserial = serial.str();
//Removing spaces
myserial.erase(std::remove_if(myserial.begin(),     myserial.end(),::isspace), myserial.end());

char buffer[sizeof(myserial) + 1];
//int ret = snprintf(buffer, sizeof(myserial), "%05ld", DlgPtr>m_lSerialNumber);
int ret = sprintf_s(buffer, sizeof(myserial), "%05ld", myserial);
const char * charSerial = buffer;
TiXmlText *textbarcode = new TiXmlText(charSerial);
barcode->LinkEndChild(textbarcode);

No error messages, just the element's tag doesn't close on the same line. I made a small sandbox project with the exact same DOM tree but it worked fine there. I was thinking maybe there is another issue I don't see with sprintf_s?? Thank you in advance.

Upvotes: 1

Views: 767

Answers (1)

Frodyne
Frodyne

Reputation: 3973

As @AlanBirtles poked at in the comments, this part of your code is not great:

stringstream serial;
serial << (DlgPtr->m_lSerialNumber);
std::string myserial = serial.str();
//Removing spaces
myserial.erase(std::remove_if(myserial.begin(),     myserial.end(),::isspace), myserial.end());

char buffer[sizeof(myserial) + 1];
int ret = sprintf_s(buffer, sizeof(myserial), "%05ld", myserial);
const char * charSerial = buffer;
TiXmlText *textbarcode = new TiXmlText(charSerial);
barcode->LinkEndChild(textbarcode);

You start out strong by converting DlgPtr->m_lSerialNumber into a std::string:

stringstream serial;
serial << (DlgPtr->m_lSerialNumber);
std::string myserial = serial.str();

This is fine, though from C++11 forward you could also just do:

std::string myserial = std::to_string(DlgPtr->m_lSerialNumber);

Then you remove spaces from myserial, which doesn't hurt - but I'm pretty sure there won't be any to remove, since you have just constructed the string from a single integer.

This is then where it starts going wrong.

You make a char-array buffer to write your number in, but you size that with sizeof(myserial), which gives you the number of bytes that the object takes up in memory, including bookkeeping stuff, vtable pointers, etc. So you wind up allocating too much space here; myserial.size() would give you a better fit.

Then you try to use sprintf_s to write the number to the new buffer, but you use "%05ld" as the format string and a std::string as the argument - that just won't work. "%05ld" tells sprintf_s to expect an integer as input (and pad it to 5 digits with extra zeros), so it just takes that argument, casts it to an int, and interprets the first 4 bytes as a number - which makes no sense, and probably invokes undefined behavior.

Not only does this not do what you want it to do, you already have a string with your number in it, and can just call myserial.c_str() to get a const char* with the contents of myserial. This means that the entire second section could be replaced by:

TiXmlText *textbarcode = new TiXmlText(std::to_string(DlgPtr->m_lSerialNumber).c_str());
barcode->LinkEndChild(textbarcode);

Upvotes: 0

Related Questions