c++ dom parser problem - c++

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

Related

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

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.

Windows Installer, Error 2896 from Custom Action after successful execution

I have a custom action dll, written in C++, which is invoked using a button during install. The purpose of the custom action is to capture the clipboard contents and evaluate whether or not the contents is in the format of a valid product key. If it is, then the 'PRODUCTKEY' property is updated, as is another property which lets me know that we have been successful.
<Control Id="PasteButton" Type="PushButton" X="25" Y="176" Width="25" Height="16" Default="yes" Text="Paste" >
<Publish Event="DoAction" Value="MsiCheckClipboardForKey" Order="1">1</Publish>
<Publish Property="PRODUCTKEY" Value="[PRODUCTKEY]" Order="2">ClipboardSuccess = 1</Publish>
</Control>
Unfortunately, the install is failing after this custom action has been called. However, from looking at the install log my properties have been changed which tends to suggest that my custom action code is running successfully.
MSI (c) (20:4C) [12:04:55:281]: Invoking remote custom action. DLL:
C:\Users\xxxxx\AppData\Local\Temp\MSI5652.tmp, Entrypoint:
msiCheckClipboardForKey
MSI (c) (20!C4) [12:04:57:746]: PROPERTY CHANGE: Modifying
ClipboardSuccess property. Its current value is '0'. Its new value:
'1'.
MSI (c) (20!C4) [12:04:57:746]: PROPERTY CHANGE: Adding PRODUCTKEY
property. Its value is 'xxxxx-xxxxx-xxxxx-xxxxx-xxxxx'.
Action ended 12:04:57: MsiCheckClipboardForKey. Return value 3.
DEBUG: Error 2896: Executing action MsiCheckClipboardForKey failed.
This is the custom action code:
#include "StdAfx.h"
#include "Debug.h"
#pragma comment(linker, "/EXPORT:msiCheckClipboardForKey=_msiCheckClipboardForKey#4")
BOOL GetClipboardText ( IN OUT CString& strClipBoardText)
{
strClipBoardText = _T("");
BOOL bOK = FALSE;
UINT uFormat = 0;
// We need to explicitly query the clipboard for UNICODE text
// if we have a UNICODE application
#ifdef _UNICODE
uFormat = CF_UNICODETEXT;
#else
uFormat = CF_TEXT;
#endif
if ( ::IsClipboardFormatAvailable ( uFormat ) )
{
if ( ::OpenClipboard ( NULL ) )
{
HANDLE hClipBrdData = NULL;
if ( HANDLE hClipBrdData = ::GetClipboardData ( uFormat ) )
{
if ( LPTSTR lpClipBrdText = ( LPTSTR ) ::GlobalLock ( hClipBrdData ) )
{
MessageBox("Clipboard Text",lpClipBrdText,NULL,NULL);
strClipBoardText = lpClipBrdText;
::GlobalUnlock ( hClipBrdData );
bOK = TRUE;
}
}
::CloseClipboard();
}
}
return bOK;
}
extern "C" UINT __stdcall msiCheckClipboardForKey(MSIHANDLE hMSI)
{
CString strClipboardText ( _T("") );
if ( GetClipboardText ( strClipboardText ) )
{
DebugMsg ( hMSI, _T("Found clipboard text") );
strClipboardText.Trim();
// Look at the length. Is it 25 (wih no dashes/slashes) or 29 (with dashes/slashes)?
BOOL bValidLength = strClipboardText.Find ( '-' ) != -1 || strClipboardText.Find ( '/' ) != -1 ? strClipboardText.GetLength() == 29 : strClipboardText.GetLength() == 25;
DebugMsg ( hMSI, _T("Is it a product key? %b",bValidLength) );
if ( bValidLength )
{
//strClipboardText.Remove ( '-' );
//strClipboardText.Remove ( '/' );
MessageBox("Formatted Clipboard Text",strClipboardText,NULL,NULL);
MsiSetProperty(hMSI, "ClipboardSuccess", "1");
MsiSetProperty(hMSI, "PRODUCTKEY", strClipboardText);
return 0;
}
}
return 1; // None-zero is error state
}
Not sure what the problem could be, even more so as the custom action does seem to be executed as the properties are set correctly.
After tidying the code to add to this question and rebuilding I found that I was only getting the error when I had a string on the clipboard which wasn't evaluated as a product key format. Therefore the custom action was returning 1. I believe this to be the reason that my install failed. A return value of 1 obviously causing the install error.
Please correct me if I am wrong but this has fixed the issue.

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.

Error handling for xml parsing

I'm using tinyxml to parse xml files, and I've found that error handling here lends itself to arrow code. Our error handling is simply reporting a message to a file.
Here is an example:
const TiXmlElement *objectType = dataRoot->FirstChildElement( "game_object" );
if ( objectType ) {
do {
const char *path = objectType->Attribute( "path" );
if ( path ) {
const TiXmlElement *instance = objectType->FirstChildElement( "instance" );
if ( instance ) {
do {
int x, y = 0;
instance->QueryIntAttribute( "x", &x );
instance->QueryIntAttribute( "y", &y );
if ( x >= 0 && y >= 0 ) {
AddGameObject( new GameObject( path, x, y ));
} else {
LogErr( "Tile location negative for GameObject in state file." );
return false;
}
} while ( instance = instance->NextSiblingElement( "instance" ));
} else {
LogErr( "No instances specified for GameObject in state file." );
return false;
}
} else {
LogErr( "No path specified for GameObject in state file." );
return false;
}
} while ( objectType = objectType->NextSiblingElement( "game_object" ));
} else {
LogErr( "No game_object specified in <game_objects>. Thus, not necessary." );
return false;
}
return true;
I'm not huffing and puffing over it, but if anyone can think of a cleaner way to accomplish this it would be appreciated.
P.S. Exceptions not an option.
Edit:
Would something like this be preferable?
if ( !path ) {
// Handle error, return false
}
// Continue
This eliminates the arrow code, but the arrow code kind of puts all of the error logging on one place.
Using return values as error codes just leads to such code, it can't be improved much. A slightly cleaner way would use goto to group all error handling into a single block and to decrease the nesting of blocks.
This does however not solve the actual problem, which is using return values as error codes. In C, there is no alternative, but in C++ exceptions are available and should be used. If they are not an option, you're are stuck with what you have.
You could create a macro for that, which encapsulates the if (!var) { .. return false; } and error reporting.
However, I do not see how this can be improved all that much; its just the way it is. C'est la vie. C'est le code...
I'm not huffing and puffing over it,
but if anyone can think of a cleaner
way to accomplish this it would be
appreciated.
I have replaced the nested ifs with return statements on error (this makes the code "flow down" instead of going "arrow shaped". I have also replaced your do loopps with for loops (so I could understand it better).
Is this what you wanted?
const TiXmlElement *objectType = dataRoot->FirstChildElement( "game_object" );
if ( !objectType ) {
LogErr( "No game_object specified in <game_objects>. Thus, not necessary." );
return false;
}
for(; objectType != 0; objectType = objectType->NextSiblingElement( "game_object" )) {
const char *path = objectType->Attribute( "path" );
if ( !path ) {
LogErr( "No path specified for GameObject in state file." );
return false;
}
const TiXmlElement *instance = objectType->FirstChildElement( "instance" );
if ( !instance ) {
LogErr( "No instances specified for GameObject in state file." );
return false;
}
for(; instance != 0; instance = instance->NextSiblingElement( "instance" )) {
int x, y = 0;
instance->QueryIntAttribute( "x", &x );
instance->QueryIntAttribute( "y", &y );
if ( x >= 0 && y >= 0 ) {
AddGameObject( new GameObject( path, x, y ));
} else {
LogErr( "Tile location negative for GameObject in state file." );
return false;
}
}
}
return true;
I know it is a little late, but I know that QueryIntAttribute returns a value which can be used for error handling in case you want this for your attributes too.
if (instance->QueryIntAttribute("x",&x)!=TIXML_SUCCESS)
cout << "No x value found";