Save the .xml file using TinyXml - c++

I want to make an xml file and I am using tinyxml to create it. I am using this code that was already online but it does not save the file.
#include "tinyxml.h"
int main()
{
build_simple_doc();
return 0;
}
void build_simple_doc( )
{
// Make xml: <?xml ..><Hello>World</Hello>
TiXmlDocument doc;
TiXmlDeclaration * decl = new TiXmlDeclaration( "1.0", "", "" );
TiXmlElement * element = new TiXmlElement( "Hello" );
TiXmlText * text = new TiXmlText( "World" );
element->LinkEndChild( text );
doc.LinkEndChild( decl );
doc.LinkEndChild( element );
doc.SaveFile( "madeByHand.xml" );
}
When it executes the last line, the file is not created. I stepped into SaveFile() and there is TiXmlFOpen() method that cannot open the file. I am not sure what I am doing wrong.

Related

C++/RapidXML: Edit node and write to a new XML file doesn't have the updated nodes

I'm parsing a XML file from a string.
My node Id is bar, and I want to change it to foo and then write to file.
After writing to file, the file still have the bar, and not the foo.
#include "rapidxml.hpp"
#include "rapidxml_print.hpp"
void main()
{
std::string newXml = "<?xml version=\"1.0\" encoding=\"UTF - 8\"?><Parent><FileId>fileID</FileId><IniVersion>2.0.0</IniVersion><Child><Id>bar</Id></Child></Parent>";
xml_document<> doc;
xml_node<> * root_node;
std::string str = newXml;
std::vector<char> buffer(str.begin(), str.end());
buffer.push_back('\0');
doc.parse<0>(&buffer[0]);
root_node = doc.first_node("Parent");
xml_node<> * node = root_node->first_node("Child");
xml_node<> * xml = node->first_node("Id");
xml->value("foo"); // I want to change my id from bar to foo!!!!
std::ofstream outFile("output.xml");
outFile << doc; // after I write to file, I still see the ID as bar
}
What am I missing here?
The issue is in the layout of data. Under node_element node xml there is yet another node_data node that contains "bar".
Your posted code also does not compile. Here I made your code to compile and did show how to fix it:
#include <vector>
#include <iostream>
#include "rapidxml.hpp"
#include "rapidxml_print.hpp"
int main()
{
std::string newXml = "<?xml version=\"1.0\" encoding=\"UTF - 8\"?><Parent><FileId>fileID</FileId><IniVersion>2.0.0</IniVersion><Child><Id>bar</Id></Child></Parent>";
rapidxml::xml_document<> doc;
std::string str = newXml;
std::vector<char> buffer(str.begin(), str.end());
buffer.push_back('\0');
doc.parse<0>(&buffer[0]);
rapidxml::xml_node<>* root_node = doc.first_node("Parent");
rapidxml::xml_node<>* node = root_node->first_node("Child");
rapidxml::xml_node<>* xml = node->first_node("Id");
// xml->value("foo"); // does change something that isn't output!!!!
rapidxml::xml_node<> *real_thing = xml->first_node();
if (real_thing != nullptr // these checks just demonstrate that
&& real_thing->next_sibling() == nullptr // it is there and how it is located
&& real_thing->type() == rapidxml::node_data) // when element does contain text data
{
real_thing->value("yuck"); // now that should work
}
std::cout << doc; // lets see it
}
And so it outputs:
<Parent>
<FileId>fileID</FileId>
<IniVersion>2.0.0</IniVersion>
<Child>
<Id>yuck</Id>
</Child>
</Parent>
See? Note that how data is laid out during parse depends on flags that you give to parse. For example if you first put doc.parse<rapidxml::parse_fastest> then parser will not create such node_data nodes and then changing node_element data (like you first tried) will work (and what I did above will not). Read the details from manual.

Deleting specific content of XML file

In my xml file i have a node whose subchilder have 2 attributes, i have to delete 1 whole subchild while considering only 1 attribute. I have given an example below
XML file:
<UMG>
<ABC Name="ABC" Value="1"></ABC>
<ABC Name="ABC1" Value="2"></ABC>
<ABC Name="ABC2" Value="3"></ABC>
<ABC Name="ABC3" Value="4"></ABC>
<ABC Name="ABC4" Value="5"></ABC>
</UMG>
I have to delete the whole subchild with only "Name" attribute, because Value can be changed.
My code until now:
void::MainWindow::XML()
{
QString path = ui->lineEdit_7->text();
qDebug()<<path;
if(!file.exists() )
{
qDebug() << "Check your file";
}
QDomDocument dom;
dom.setContent(&file);
QDomNodeList nodes = dom.elementsByTagName("ABC");
QDomNodeList loc_childNodes = nodes.at(0).childNodes();
for(int i=0; i<loc_childNodes.count(); i++)
{
QDomNode node = loc_childNodes.at(i);
qDebug() << node.attributes().namedItem("Name").nodeValue(); // I get all Name attributes.
last qDebug gives me all "Name" attributes. I am stuck at deleting the subchild with using this information.
Edit:
<NEW>
<child>ABC<child>
<child1>ABC1<child1>
<Child3>ABC3<child3>
<NEW>
EDIT2:
<MAIN>
<SUB Name = "ABC" Value = "1"/>
<SUB Name = "ABC1" Value = "0"/>
<SUB Name = "ABC2" Value = "3"/>
<Header Name = "Abc" value = "9"/>
<SUB Name = "ABC7" Value = "3"/>
<Header Name = "Abc5" value = "9"/>
<SUB Name = "ABC3" Value = "3"/>
<Header Name = "Abc0" value = "9"/>
</MAIN>
I want to delete only "SUB" attributes child.
EXPECTED Result:
<MAIN>
<Header Name = "Abc" value = "9"/>
<Header Name = "Abc5" value = "9"/>
<Header Name = "Abc0" value = "9"/>
</MAIN>
EDit3:
qDebug()<<manualoutput_scr;
QString path = "File"
QFile inFile(path );
if( !inFile.open( QIODevice::ReadOnly | QIODevice::Text ) )
{
qDebug( "Failed to open file for reading." );
}
QDomDocument dom;
if( !dom.setContent( &inFile ) )
{
qDebug( "Failed to parse the file into a DOM tree." );
}
QDomElement docElem = dom.documentElement();
QDomNodeList nodes = docElem.elementsByTagName("MAIN");
QDomNodeList loc_childNodes = nodes.at(0).childNodes();
for(int i=0; i<loc_childNodes.count(); i++)
{
QDomNode node = loc_childNodes.at(i);
if( node.nodeName().compare("SUB") == 0 ) {
QDomNode parentNode = node.parentNode();
parentNode.removeChild(node);
}
}
QFile outFile( path);
if( !outFile.open( QIODevice::WriteOnly | QIODevice::Text ) )
{
qDebug( "Failed to open file for writing." );
}
QTextStream stream( &outFile );
stream << dom.toString();
outFile.close();
QDomNode has method removeChild may be it help?
From doc
QDomNode QDomNode::removeChild(const QDomNode & oldChild)
Removes oldChild from the list of children. oldChild must be a direct
child of this node. Returns a new reference to oldChild on success or
a null node on failure.
Addition Your code may looks something like follow
if( node.attributes().namedItem("Name").nodeValue().compare("ABC3") == 0 ) {
QDomNode parentNode = node.parentNode();
parentNode.removeChild(node);
}
Addition to Edit 2
if( node.nodeName().compare("SUB") == 0 ) {
QDomNode parentNode = node.parentNode();
parentNode.removeChild(node);
}
Update To Edit 3. Replace the lines
QDomElement docElem = dom.documentElement();
QDomNodeList nodes = docElem.elementsByTagName("MAIN");
with
QDomNodeList nodes = dom.elementsByTagName("MAIN");
and after parent.removeChild(node) add i-=1, because a count of elements is decreased.
And don't forget to close the file ("File") inFile.close() before to call outFile.open()

Why can't I copy the contents of an XMLDocument to another XMLDocument using TinyXml2?

I do not understand why the below code doesn't work as intended to copy the one element from doc1 to doc2:
void test_xml(){
using namespace tinyxml2;
XMLDocument doc1, doc2;
XMLPrinter printer;
doc1.LoadFile("./res/xml/basic.xml");
if(doc1.Error())
std::cout << doc1.ErrorName();
doc1.Print(&printer);
std::cout << printer.CStr(); // prints "</atag>" without quotes
printer.ClearBuffer();
doc2.InsertFirstChild(doc1.RootElement());
if(doc2.Error())
std::cout << doc2.ErrorName(); // doesn't run, there's no error
doc2.Print(&printer);
std::cout << printer.CStr(); // prints nothing, no child got inserted to doc2
std::cout << doc2.NoChildren(); //prints "1" meaning we didn't insert anything
}
Can someone point out how this can be improved?
From the TinyXml2 documentation:
InsertFirstChild ... Returns the addThis argument or 0 if the node does not belong to the same document.
Basically you can only add a node to a document if the node was manufactured by that document (with NewElement, NewText etc).
You have to walk through doc1 creating corresponding nodes as you go (with ShallowClone, and adding them to doc2. It appears there is no DeepClone to do it all for you.
At http://sourceforge.net/p/tinyxml/discussion/42748/thread/820b0377/, "practisevoodoo" suggests:
XMLNode *deepCopy( XMLNode *src, XMLDocument *destDoc )
{
XMLNode *current = src->ShallowClone( destDoc );
for( XMLNode *child=src->FirstChild(); child; child=child->NextSibling() )
{
current->InsertEndChild( deepCopy( child, destDoc ) );
}
return current;
}

xml parsing in c++ using tinyxml

i am creating xml file in c++
i wrote the code like creating writing the xml file in c++ as shown below
const char* assign =
"<?xml version=\"1.0\" standalone='no' >\n"
"<office>"
"<work>file created</work>"
"</office>";
TiXmlDocument doc( "vls.xml" );
doc.Parse( assign );
if ( doc.Error() )
{
printf( "Error in %s: %s\n", doc.Value(), doc.ErrorDesc() );
exit( 1 );
}
doc.SaveFile();
bool loadOkay = doc.LoadFile();
if ( !loadOkay )
{
printf( "Could not load test file 'vls.xml'. Error='%s'. Exiting.\n", doc.ErrorDesc() );
exit( 1 );
}
else
printf(" 'vls.xml' loaded successfully");
but now i need only the data in the XMl file not the tags
guys plz help me.
I'd suggest reading the TinyXml documentation, more specifically the TiXmlElement documentation.
For your special case, I'd say it looks like that:
TiXmlElement * office = doc.FirstChildElement( "office" );
if( office )
{
TiXmlElement *work = office.FirstChildElement( "work" );
if( work )
{
printf("Work text: %s\n", work.GetText());
}
}
Although I'm not an expert with TinyXml.
FYI:
StackOverflow search function
Some - Answers - You - should have - considered ....
Please, search Google and StackOverflow before asking such trivial questions.

c++ dom parser problem

I want to change an XML file. I'm using DOM Parser. My XML file is the following:
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<!-- Put site-specific property overrides in this file. -->
<configuration>
<property>
<name>fs.default.name</name>
<value> name</value>
</property>
</configuration>
I just want to remove the <value>name</name> node and put a new node <value>next</value>. How can I do this?
I wrote code in C++ also, but I'm stuck in the middle. What should I do? My code is the following:
#include<string.h>
#include<iostream>
#include<sstream>
#include<sys/types.h>
#include<unistd.h>
#include<errno.h>
#include<sys/stat.h>
#include "parser.hpp"
using namespace xercesc ;
using namespace std;
GetConfig::GetConfig()
{
XMLPlatformUtils::Initialize();
TAG_configuration =XMLString::transcode("configuration");
TAG_property = XMLString::transcode("property");
TAG_value=XMLString::transcode("value");
Tag_name=XMLString::transcode("name");
m_ConfigFileParser=new XercesDOMParser;
}
GetConfig::~GetConfig()
{
delete m_ConfigFileParser;
XMLString::release( &TAG_configuration );
XMLString::release( &TAG_property );
XMLString::release( &TAG_value );
XMLPlatformUtils::Terminate();
}
void GetConfig :: readConfigFile(string& configFile)
{
struct stat fileStatus;
int iretStat = stat(configFile.c_str(), &fileStatus);
if( iretStat == ENOENT )
throw ( std::runtime_error("Path file_name does not exist, or path is an empty string.") );
else if( iretStat == ENOTDIR )
throw ( std::runtime_error("A component of the path is not a directory."));
else if( iretStat == ELOOP )
throw ( std::runtime_error("Too many symbolic links encountered while traversing the path."));
else if( iretStat == EACCES )
throw ( std::runtime_error("Permission denied."));
else if( iretStat == ENAMETOOLONG )
throw ( std::runtime_error("File can not be read\n"));
// Configure DOM parser.
m_ConfigFileParser->setValidationScheme( XercesDOMParser::Val_Never );
m_ConfigFileParser->setDoNamespaces( false );
m_ConfigFileParser->setDoSchema( false );
m_ConfigFileParser->setLoadExternalDTD( false );
m_ConfigFileParser->parse( configFile.c_str() );
DOMDocument* xmlDoc = m_ConfigFileParser->getDocument();
DOMElement* elementRoot = xmlDoc->getDocumentElement();
DOMNodeList* children = elementRoot->getChildNodes();
int main()
{
string configFile="/home/manish.yadav/Desktop/simple.xml";
GetConfig appConfig;
appConfig.readConfigFile(configFile);
return 0;
}
Now I don't know how to traverse this document. Here are my questions:
How can I reach to <value>?
How can I change value of <value> name</value> to <value> next</value>?
My idea is to remove the entity and then add it again with different value, but I don't know how to do this, either. Please explain with example code, or suggest any other ideas on how to do this.
After m_ConfigFileParser->parse( configFile.c_str() ); do the following (considering "configuration" is the root element):
DOMDocument* doc = m_ConfigFileParser.getDocument();
DOMElement* root = dynamic_cast<DOMElement*>( doc->getFirstChild() );
if ( root ) {
DOMElement* property = dynamic_cast<DOMElement*>( root->getElementsByTagName( "property" )->item( 0 ) );
if ( property ) {
DOMElement* value = dynamic_cast<DOMElement*>( property->getElementsByTagName( "value" )->item( 0 ) );
if ( value ) {
value->setTextContent( " next" ); // this will update the element named "value"
}
}
}