Deleting specific content of XML file - c++

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()

Related

Save the .xml file using TinyXml

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.

Lastchild in xml file using QDomDocument Class

i have this xml:
<VCAAnalysis>
<VCAStream>
<VCAFrame width="768" height="432" rtptime="" utctime="102157000" utctimeHigh="0" configID="0" />
<VCAFrame width="768" height="432" rtptime="" utctime="102157160" utctimeHigh="0" configID="0">
<Object objectID="138.96.200.59_20160126_102157160_1" minX="276" minY="0" maxX="320" maxY="123" width="44" height="123" ObjPropTag="PERSON">
</Object>
</VCAFrame>
<VCAFrame width="768" height="432" rtptime="" utctime="102157320" utctimeHigh="0" configID="0" />
<VCAFrame width="768" height="432" rtptime="" utctime="102157480" utctimeHigh="0" configID="0">
<Object objectID="138.96.200.59_20160126_102157480_2" minX="224" minY="264" maxX="287" maxY="343" width="63" height="79" ObjPropTag="PERSON">
</Object>
</VCAFrame>
<VCAFrame width="768" height="432" rtptime="" utctime="102157640" utctimeHigh="0" configID="0">
<Object objectID="138.96.200.59_20160126_102157480_3" minX="204" minY="266" maxX="331" maxY="400" width="127" height="134" ObjPropTag="PERSON">
</Object>
</VCAFrame>
<VCAFrame width="768" height="432" rtptime="" utctime="102157000" utctimeHigh="0" configID="0" />
</VCAStream>
</VCAAnalysis>
I want to get the last objectID(138.96.200.59_20160126_102157480_3) in the last VCAFrame which have an object.
i tried this code but it doesn't work.
QDomNodeList a = VCAStream.elementsByTagName("VCAFrame");
if(a.size()!=0) {
QDomElement lastobj = VCAStream.lastChild().toElement();
QDomElement last = lastobj.firstChild().toElement();
QString lastid = last.attribute("objectID");
cout << qPrintable("laaaaaaaast "+lastid) << endl;
}
This worked for me:
QDomNodeList vcaStreams = VCAStream.elementsByTagName("VCAStream");
QDomNodeList vcaFrames = vcaStreams.at(0).childNodes(); //Gives 6 VCAFrame tags
QDomNodeList vcaObjects = vcaFrames.at(4).childNodes(); //Gives 1 Object tag
qDebug() << vcaObjects.at(0).toElement().attribute("objectID");
lastobj in your code refers to the last VCAFrame, which does not have an objectID.
EDIT: If you need to iterate over an entire xml file. I'm assuming that you want the last vcaFrame that has an objectID in each VCAStream.
QDomNodeList vcaStreams = VCAStream.elementsByTagName("VCAStream");
for (int i = 0; i < vcaStreams.count(); ++i) {
QDomNodeList vcaFrames = vcaStreams.at(i).childNodes(); //Gives us all VCAFrameTags
//Find last tag with objectID
QDomElement last;
for (int j = vcaFrames.count() - 1; j >= 0; --j) {
//Assumes there is at most one <object> tag in each VCAFrame
if (vcaFrames.at(j).hasChildNodes()) {
QDomElement tmp = vcaFrames.at(j).firstChild().toElement();
if (tmp.hasAttribute("objectID")) {
last = tmp;
break;
}
}
}
//last now holds the last VCAFrame with an object tag or is Null
if (last.isNull())
qDebug() << "No objectID found";
else
qDebug() << last.attribute("objectID");
}
I tested this on your XML file and it gave me the correct result, but I did not try adding more than one VCAStream tag.

Qt Read XML File

I am trying to read an xml file in Qt, which I successfully generated using a different method. Here is my xml file:
<?xml version="1.0" encoding="UTF-8"?>
<Project>
<EditorTheme>NULL</EditorTheme>
<Modules>
<Module>
<Name>Module_Renderer</Name>
<Position>471,164</Position>
<Size>200,100</Size>
<Locked>true</Locked>
<Visible>true</Visible>
</Module>
<Module>
<Name>Module_Console</Name>
<Position>200,229</Position>
<Size>256,192</Size>
<Locked>true</Locked>
<Visible>false</Visible>
</Module>
<Module>
<Name>Module_ResourceToolkit</Name>
<Position>1049,328</Position>
<Size>200,100</Size>
<Locked>true</Locked>
<Visible>true</Visible>
</Module>
<Module>
<Name>Module_CellEditor</Name>
<Position>542,564</Position>
<Size>200,100</Size>
<Locked>true</Locked>
<Visible>false</Visible>
</Module>
</Modules>
</Project>
And here is some code that I am using to parse this file:
Project ProjectLoader::loadLastProject( ConsoleModule* console ) {
Project project;
// load xml
QFile file( "C:/Users/Krynn/Desktop/LastProject.xml" );
if( !file.open( QFile::ReadOnly | QFile::Text ) ) {
// print error cannot open
}
QXmlStreamReader reader;
console->outputDisplay->append( "Test" );
reader.setDevice( &file );
reader.readNext();
while( !reader.atEnd() && !reader.hasError() ) {
reader.readNext();
if( reader.isStartElement() ) {
QString name = reader.name().toString();
if( reader.name() == "Project" ) {
reader.readNextStartElement();
if( reader.name().toString() == "EditorTheme" ) {
// Append Project theme
console->outputDisplay->append( "Theme Detected: " + reader.name().toString() + " " + reader.readElementText() );
}
reader.readNextStartElement();
if( reader.name().toString() == "Modules" ) {
// how do I proceed??
console->outputDisplay->append( QString( "" ) + " " + reader.name().toString() + " " + reader.readElementText() );
}
}
}
}
if( reader.hasError() ) {
console->outputDisplay->append( "XML error: " + reader.errorString() );
} else if( reader.atEnd() ) {
console->outputDisplay->append( "End of XML File Reached" );
}
file.close();
return project;
}
And here is some visual output for what that code gives me:
Really, I just don't know how I would go about loading all the module data within the xml file. I was using a plain text file previously to store all this stuff, but now I want to upgrade. Any help would be greatly appreciated.
Nevermind I figured it out.
Project ProjectLoader::loadLastProject( ConsoleModule* console ) {
Project project;
// load xml
QFile file( "C:/Users/Krynn/Desktop/LastProject.xml" );
if( !file.open( QFile::ReadOnly | QFile::Text ) ) {
// print error cannot open
}
QXmlStreamReader reader;
reader.setDevice( &file );
reader.readNext();
int count = 0;
while( !reader.atEnd() ) { //&& !reader.hasError()
reader.readNext();
if( reader.isStartElement() ) {
if( reader.name().toString() == "Module" ) {
WindowModuleSaveData data;
reader.readNextStartElement();
data.name = reader.readElementText(); // name
reader.readNextStartElement();
data.position = convertStringToQPoint( reader.readElementText() );
console->outputDisplay->append( convertQPointToString(data.position) );
reader.readNextStartElement();
data.size = convertStringToQSize( reader.readElementText() );
reader.readNextStartElement();
data.isLocked = reader.readElementText() == "true" ? true : false;
reader.readNextStartElement();
data.isVisible = reader.readElementText() == "true" ? true : false;
project.modules.push_back( data );
console->outputDisplay->append("Loaded A Module");
}
count++;
}
}
console->outputDisplay->append( QString::number( count ) );
if( reader.hasError() ) {
console->outputDisplay->append( "XML error: " + reader.errorString() );
} else if( reader.atEnd() ) {
console->outputDisplay->append( "End of XML File Reached" );
}
file.close();
return project;
}
The above code may be error prone, because it assumes what the next child may be instead of actually testing for it. Good enough for now though.

QTDom - recursively add child to specific elements

I have few xml files, and some of it's nodes contains "reference" to each other. I want to add content of this xml as child to nodes, that contains this references
void GameObject::ExpandNode(QDomElement& expNode)
{
if ((expNode.tagName() == "Instance" && expNode.attribute("type") == "library") ||
(expNode.tagName() == "library" && expNode.hasAttribute("file")))
{
QString fileName;
if (expNode.tagName() == "Instance" )
fileName = FindLib(expNode.attribute("definition")).attribute("file");
else
fileName = expNode.attribute("file");
QFile nestedFile(fileName);
QDomDocument nestedLib;
nestedLib.setContent(&nestedFile);
QDomElement nestedNode = libDoc->createElement("NestedLibrary");
nestedNode.setAttribute("path", fileName);
nestedNode.appendChild(nestedLib);
expNode.appendChild(nestedNode);
}
QDomNode childNode = expNode.firstChild();
while (!childNode.isNull())
{
if (childNode.isElement())
ExpandNode(childNode.toElement());
childNode = childNode.nextSibling();
}
}
But what i got is
no matching function for call to 'GameObject::ExpandNode(QDomElement)' ExpandNode(childNode.toElement());
How can i do this right?
^
It was wrong decision - to call ExpandNode, using temporary object. Solution is
QDomNode childNode = expNode.firstChild();
while (!childNode.isNull())
{
if (childNode.isElement())
{
QDomElement childElem = childNode.toElement();
ExpandNode(childElem);
}
childNode = childNode.nextSibling();
}
isElement : If this function returns true, it does not imply that this object is a QDomElement;
do this
QDomElement node = childNode.toElement;
if (node != NULL)
{
ExpandNode(node);
}

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"
}
}
}