tinyxml2 building and writing xml file on c++ - c++

I am trying to build a simple xml file using tinyxml2 in VS2019,
for some reason the code works until it hits the element named port.
this element and every element after it is ignored in the xml file.
I just noticed, it is also writing xml output in the wrong order, host should be the one right below , not port.
What am i missing here?
Truth be told, i have about 2 days experience writing c++, and very basic python knowlage.
#include <iostream>
#include <fstream>
#include <windows.h>
#include <Lmcons.h>
#include <stdlib.h>
#include <filesystem>
#include "tinyxml/tinyxml2.h"
using namespace tinyxml2;
using namespace std;
namespace fs = std::filesystem;
int main()
{
SetConsoleOutputCP(CP_UTF8);
char* appdata;
size_t len;
errno_t err = _dupenv_s(&appdata, &len, "APPDATA");
fs::path path = appdata;
path /= "FileZilla";
path /= "sitemanager.xml";
TCHAR username[UNLEN + 1];
DWORD size = UNLEN + 1;
GetUserName((TCHAR*)username, &size);
tinyxml2::XMLDocument xmlDoc;
//tinyxml2::XMLDeclaration* decl = new XMLDeclaration("1.0", "UTF-8", "");
XMLElement* pRoot = xmlDoc.NewElement("FileZilla3");
pRoot->SetAttribute("Version", "");
pRoot->SetAttribute("Platform", "");
xmlDoc.InsertFirstChild(pRoot);
XMLElement* child = xmlDoc.NewElement("Servers");
child->SetText("\n");
pRoot->InsertEndChild(child);
XMLElement* subchild = xmlDoc.NewElement("Server");
subchild->SetText("\n");
child->InsertEndChild(subchild);
XMLElement* host = xmlDoc.NewElement("host");
host->SetText("ftp.some.url");
subchild->InsertEndChild(host);
XMLElement* port = xmlDoc.NewElement("port");
port->SetText(21);
subchild->InsertEndChild(port);
XMLElement* protocol = xmlDoc.NewElement("Protocol");
protocol->SetText(0);
subchild->InsertEndChild(protocol);
XMLElement* type = xmlDoc.NewElement("type");
type->SetText(0);
subchild->InsertEndChild(type);
XMLElement* user = xmlDoc.NewElement("user");
user->SetText("test");
subchild->InsertEndChild(host);
xmlDoc.SaveFile("SavedData.xml");
cout << path << endl;
std::wcout << username << endl;
return 0;
}
The output file looks like this:
<FileZilla3 Version="" platform="">
<Servers>
<Server>
<port>21</port>
<Protocol>0</Protocol>
<type>0</type>
<host>ftp.some.url</host>
</Server>
</Servers>
</FileZilla3>
the desired uotput should be this:
<?xml version="1.0" encoding="UTF-8"?>
<FileZilla3 version="" platform="">
<Servers>
<Server>
<Host>saddf</Host>
<Port>21</Port>
<Protocol>0</Protocol>
<Type>0</Type>
<User>username</User>
<Pass encoding="base64" />
<Logontype>1</Logontype>
<PasvMode>MODE_DEFAULT</PasvMode>
<EncodingType>Auto</EncodingType>
<BypassProxy>0</BypassProxy>
<Name>Ny tjener</Name>
<SyncBrowsing>0</SyncBrowsing>
<DirectoryComparison>0</DirectoryComparison>
</Server>
</Servers>
</FileZilla3>

XMLElement* user = xmlDoc.NewElement("user");
user->SetText("test");
subchild->InsertEndChild(host);
should be
XMLElement* user = xmlDoc.NewElement("user");
user->SetText("test");
subchild->InsertEndChild(user);

Related

C++ how to find file path of the file I am working on in my program [duplicate]

I want to create a file in the current directory (where the executable is running).
My code:
LPTSTR NPath = NULL;
DWORD a = GetCurrentDirectory(MAX_PATH,NPath);
HANDLE hNewFile = CreateFile(NPath,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
I get exception at GetCurrentDirectory().
Why am I getting an exception?
I would recommend reading a book on C++ before you go any further, as it would be helpful to get a firmer footing. Accelerated C++ by Koenig and Moo is excellent.
To get the executable path use GetModuleFileName:
TCHAR buffer[MAX_PATH] = { 0 };
GetModuleFileName( NULL, buffer, MAX_PATH );
Here's a C++ function that gets the directory without the file name:
#include <windows.h>
#include <string>
#include <iostream>
std::wstring ExePath() {
TCHAR buffer[MAX_PATH] = { 0 };
GetModuleFileName( NULL, buffer, MAX_PATH );
std::wstring::size_type pos = std::wstring(buffer).find_last_of(L"\\/");
return std::wstring(buffer).substr(0, pos);
}
int main() {
std::cout << "my directory is " << ExePath() << "\n";
}
The question is not clear whether the current working directory is wanted or the path of the directory containing the executable.
Most answers seem to answer the latter.
But for the former, and for the second part of the question of creating the file, the C++17 standard now incorporates the filesystem library which simplifies this a lot:
#include <filesystem>
#include <iostream>
std::filesystem::path cwd = std::filesystem::current_path() / "filename.txt";
std::ofstream file(cwd.string());
file.close();
This fetches the current working directory, adds the filename to the path and creates an empty file. Note that the path object takes care of os dependent path handling, so cwd.string() returns an os dependent path string. Neato.
GetCurrentDirectory does not allocate space for the result, it's up to you to do that.
TCHAR NPath[MAX_PATH];
GetCurrentDirectory(MAX_PATH, NPath);
Also, take a look at Boost.Filesystem library if you want to do this the C++ way.
An easy way to do this is:
int main(int argc, char * argv[]){
std::cout << argv[0];
std::cin.get();
}
argv[] is pretty much an array containing arguments you ran the .exe with, but the first one is always a path to the executable. If I build this the console shows:
C:\Users\Ulisse\source\repos\altcmd\Debug\currentdir.exe
IMHO here are some improvements to anon's answer.
#include <windows.h>
#include <string>
#include <iostream>
std::string GetExeFileName()
{
char buffer[MAX_PATH];
GetModuleFileName( NULL, buffer, MAX_PATH );
return std::string(buffer);
}
std::string GetExePath()
{
std::string f = GetExeFileName();
return f.substr(0, f.find_last_of( "\\/" ));
}
#include <iostream>
#include <stdio.h>
#include <dirent.h>
std::string current_working_directory()
{
char* cwd = _getcwd( 0, 0 ) ; // **** microsoft specific ****
std::string working_directory(cwd) ;
std::free(cwd) ;
return working_directory ;
}
int main(){
std::cout << "i am now in " << current_working_directory() << endl;
}
I failed to use GetModuleFileName correctly. I found this work very well.
just tested on Windows, not yet try on Linux :)
WCHAR path[MAX_PATH] = {0};
GetModuleFileName(NULL, path, MAX_PATH);
PathRemoveFileSpec(path);
Please don't forget to initialize your buffers to something before utilizing them. And just as important, give your string buffers space for the ending null
TCHAR path[MAX_PATH+1] = L"";
DWORD len = GetCurrentDirectory(MAX_PATH, path);
Reference
You should provide a valid buffer placeholder.
that is:
TCHAR s[100];
DWORD a = GetCurrentDirectory(100, s);
#include <windows.h>
using namespace std;
// The directory path returned by native GetCurrentDirectory() no end backslash
string getCurrentDirectoryOnWindows()
{
const unsigned long maxDir = 260;
char currentDir[maxDir];
GetCurrentDirectory(maxDir, currentDir);
return string(currentDir);
}
You can remove the filename from GetModuleFileName() with more elegant way:
TCHAR fullPath[MAX_PATH];
TCHAR driveLetter[3];
TCHAR directory[MAX_PATH];
TCHAR FinalPath[MAX_PATH];
GetModuleFileName(NULL, fullPath, MAX_PATH);
_splitpath(fullPath, driveLetter, directory, NULL, NULL);
sprintf(FinalPath, "%s%s",driveLetter, directory);
Hope it helps!
GetCurrentDirectory() gets the current directory which is where the exe is invoked from. To get the location of the exe, use GetModuleFileName(NULL ...). if you have the handle to the exe, or you can derive it from GetCommandLine() if you don't.
As Mr. Butterworth points out, you don't need a handle.
Why does nobody here consider using this simple code?
TCHAR szDir[MAX_PATH] = { 0 };
GetModuleFileName(NULL, szDir, MAX_PATH);
szDir[std::string(szDir).find_last_of("\\/")] = 0;
or even simpler
TCHAR szDir[MAX_PATH] = { 0 };
TCHAR* szEnd = nullptr;
GetModuleFileName(NULL, szDir, MAX_PATH);
szEnd = _tcsrchr(szDir, '\\');
*szEnd = 0;
I guess, that the easiest way to locate the current directory is to cut it from command line args.
#include <string>
#include <iostream>
int main(int argc, char* argv[])
{
std::string cur_dir(argv[0]);
int pos = cur_dir.find_last_of("/\\");
std::cout << "path: " << cur_dir.substr(0, pos) << std::endl;
std::cout << "file: " << cur_dir.substr(pos+1) << std::endl;
return 0;
}
You may know that every program gets its executable name as first command line argument. So you can use this.
Code snippets from my CAE project with unicode development environment:
/// #brief Gets current module file path.
std::string getModuleFilePath() {
TCHAR buffer[MAX_PATH];
GetModuleFileName( NULL, buffer, MAX_PATH );
CT2CA pszPath(buffer);
std::string path(pszPath);
std::string::size_type pos = path.find_last_of("\\/");
return path.substr( 0, pos);
}
Just use the templete CA2CAEX or CA2AEX which calls the internal API ::MultiByteToWideChar or ::WideCharToMultiByte。
if you don't want to use std, you can use this code:
char * ExePath()
{
static char buffer[MAX_PATH] = { 0 };
GetModuleFileName( NULL, buffer, MAX_PATH );
char * LastSlash = strrchr(buffer, '\\');
if(LastSlash == NULL)
LastSlash = strrchr(buffer, '/');
buffer[LastSlash-buffer] = 0;
return buffer;
}
I simply use getcwd() method for that purpose in Windows, and it works pretty well. The code portion is like following:
char cwd[256];
getcwd(cwd, 256);
string cwd_str = string(cwd);
The <unistd.h> library has to be added though.
To find the directory where your executable is, you can use:
TCHAR szFilePath[_MAX_PATH];
::GetModuleFileName(NULL, szFilePath, _MAX_PATH);
If you are using the Poco library, it's a one liner and it should work on all platforms I think.
Poco::Path::current()
On a give Windows C++ IDE I went crude and it was simple, reliable, but slow:
system( "cd" );
String^ exePath = Application::ExecutablePath;<br>
MessageBox::Show(exePath);
In Windows console, you can use the system command CD (Current Directory):
std::cout << "Current Directory = ";
system("cd"); // to see the current executable directory

XML Parser not showing informations C++

I am using TinyXML2 and I got some strange problem. So It seems it work because I dont have any crashes but when I want to output one of the value I got I have nothing , if someone know why ?
I already used tinyxml2 and it was working but here maybe the XML is strange?
#include <iostream>
#include "tinyxml2/tinyxml2.cpp"
#include "../Inc/classConfigCtx.h"
#include "../Inc/globalDefine.h"
using namespace tinyxml2;
int main() {
std::cout << "Hello World!";
XMLDocument doc;
doc.LoadFile( "NetworkConfig.xml" );
int AppliMsgPort_;
const char * portAppli;
const char * IPtemp;
XMLElement * pRootElement =doc.RootElement()->FirstChildElement();
portAppli=pRootElement->FirstChildElement()->GetText();
std::cout<<portAppli;
return 0;
}
and my XML
<configuration>
<operational_00001000><name value="100.CBD" /><radio_1><type value="Net" /><ip value="10.36.1.1" /><port_s value="50010" /><port_r value="50100" /><radioID value="10040" /></radio_1></operational_00001000>
<operational_00001005><name value="100.LOG" /><radio_1><type value="Net" /><ip value="10.26.2.1" /><port_s value="50000" /><port_r value="50000" /><radioID value="1000005" /></radio_1></operational_00001005>
</configuration>

RapidXml Work when build but have problems in debugger

Hi i use rapidxml to load map in my game this is how my class for loading looks like
it's normally compiling but when loading file sometimes it crashing so i wanted to debug it but debugger dont like my function which is setting pointers to data in xml file.
#0 0042D5A6 rapidxml::xml_node<char>::first_node(this=0x0, name=0x484175 <_ZSt16__convert_from_vRKPiPciPKcz+4735349> "MapInfo", name_size=7, case_sensitive=true) (C:/.../rapidxml.hpp:942)
#1 00404E31 MapLoader::SetNodes(this=0x27fc1c) (C:\...\main.cpp:651)
#2 004032F6 main() (C:\...\main.cpp:267)
class MapLoader
{
public:
xml_document<> doc;
file<>xmlFile(char);
string ca,cb,cc,cd;
xml_node<> *test;
xml_node<> *root;
xml_node<> *mapinfo;
xml_node<> *name;
xml_node<> *date;
xml_node<> *msize;
xml_attribute<> *sizex;
xml_attribute<> *sizey;
xml_node<> *mapdata;
xml_node<> *layer;
xml_attribute<> *nr;
xml_node<> *tile;
xml_attribute<> *id;
xml_attribute<> *x;
xml_attribute<> *y;
void LoadFile(const char *filename);
void SetNodes();
void FillVector();
void SaveVector();
};
void MapLoader::SetNodes()
{
root=doc.first_node("root");
mapinfo=root->first_node("MapInfo"); //////debugger is pointing on this line
name=mapinfo->first_node("Name");
date=mapinfo->first_node("Date");
msize=mapinfo->first_node("Size");
sizex=msize->first_attribute("x");
sizey=msize->first_attribute("y");
mapdata=root->first_node("MapData");
layer=mapdata->first_node("Layer");
nr=layer->first_attribute("id");
tile=layer->first_node("Tile");
id=tile->first_attribute("id");
x=tile->first_attribute("x");
y=tile->first_attribute("y");
}
what i can do to repair it or something like that?
Edit:
here is my xml file:
<?xml version="1.0" encoding="utf-8"?>
<root>
<MapInfo>
<Name>Test</Name>
<Date>17.08.2014</Date>
<Size x="64" y="64"/>
</MapInfo>
<MapData>
<Layer nr="1">
<Tile id="1" x="32" y="32"/>
<Tile id="1" x="32" y="64"/>
<Tile id="1" x="512" y="64"/>
</Layer>
<Layer nr="2"/>
<Layer nr="3"/>
</MapData>
</root>
First of all its good practice to check for NULL as returned pointer. My guess would be, that "root" is missing in the xml file.
Take a look at the Manual of rapidxml because case sensitivity can also be an issue.
Edit:
I wrote a small program and checked at least Name and Date. Works for me.
// Trying out rapid xml.
//
#include "rapidxml/rapidxml.hpp"
#include <cstdio>
#include <string>
#include <vector>
int main(int argc, char** argv) {
rapidxml::xml_document<> doc; // character type defaults to char
FILE* file = fopen("Test.xml", "r");
if (!file)
return 1;
// file exists.
// get the number of bytes.
fseek(file, 0, SEEK_END);
size_t sizeInBytes = ftell(file);
fseek(file, 0, SEEK_SET);
char* buffer = static_cast<char*>(malloc(sizeInBytes + 1)); // + 1 needed?
if (fread(buffer, 1LU, sizeInBytes, file) != sizeInBytes) {
perror("unexpected file length\n");
fclose(file);
free(buffer);
return 1;
}
buffer[sizeInBytes] = 0;
// close the file.
fclose(file);
doc.parse<0>(buffer); // 0 means default parse flags
std::vector<std::string> nodeNames;
nodeNames.push_back("Name");
nodeNames.push_back("Date");
rapidxml::xml_node<>* node = doc.first_node("root");
for (size_t i = 0; i < nodeNames.size() && node; ++i) {
if (node->first_node(nodeNames[i].c_str()))
fprintf(stderr, "CantFindNode with name %s.\n", nodeNames[i].c_str());
printf("Node %s found!\n", nodeNames[i].c_str());
}
free(buffer);
return 0;
}

UTF-8 and TinyXML

For some reason I can not read data from a xml file properly.
For example instead of "Schrüder" I get something like "Schrüder".
My code:
tinyxml2::XMLDocument doc;
bool open(string path) {
if(doc.LoadFile(path.c_str()) == XML_SUCCESS)
return true;
return false;
}
int main() {
if(open("C:\\Users\\Admin\\Desktop\\Test.xml"))
cout << "Success" << endl;
XMLNode * node = doc.RootElement();
string test = node->FirstChild()->GetText();
cout << test << endl;
return 0;
}
Part of XML:
<?xml version="1.0" encoding="UTF-8"?>
<myXML>
<my:TXT_UTF8Test>Schrüder</my:TXT_UTF8Test>
</myXML>
Notice that if I convert it to ANSI and change the encoding type to "ISO-8859-15" it works fine.
I read that something like "LoadFile( filename, TIXML_ENCODING_UTF8 )" should help. However that's not the case (error: Invalid arguments, it just expects a const char). I have the latest version of TinyXML2 (I guess?). I downloaded it just a couple minutes ago from https://github.com/leethomason/tinyxml2.
Any ideas?
Edit: When I write the string to a .xml or .txt file it works fine. There might be some problem with the eclipse ide console. Anyway, when I try to send the string via E-Mail, I also get the same problems. Here's the MailSend script:
bool sendMail(std::string params) {
if( (int) ShellExecute(NULL, "open", "H:\\MailSend\\MailSend_anhang.exe", params.c_str(), NULL, SW_HIDE) <= 32 )
return false;
return true;
}
I call it in the main method like this:
sendMail("-f:d.nitschmann#example.com -t:person2#example.com -s:Subject -b:Body " + test);
I think the problem is with your terminal; can you try run your test code in a different terminal ? one with known good UTF-8 support ?
Output with terminal in UTF-8 mode:
$ ./a.out
Success
Schrüder
Output with terminal in ISO-8859-15 mode:
$ ./a.out
Success
SchrÃŒder
Also - please try and follow http://sscce.org/ - for posterity sake here is your code with everything needed to compile (17676169.cpp):
#include <tinyxml2.h>
#include <string>
#include <iostream>
using namespace std;
using namespace tinyxml2;
tinyxml2::XMLDocument doc;
bool open(string path) {
if(doc.LoadFile(path.c_str()) == XML_SUCCESS)
return true;
return false;
}
int main() {
if(open("Test.xml"))
cout << "Success" << endl;
XMLNode * node = doc.RootElement();
string test = node->FirstChildElement()->GetText();
cout << test << endl;
return 0;
}
compiled with:
g++ -o 17676169 17676169.cpp -ltinyxml2
and uuencoded Test.xml - to ensure exact same data is used
begin 660 Test.xml
M/#]X;6P#=F5R<VEO;CTB,2XP(B!E;F-O9&EN9STB551&+3#B/SX*/&UY6$U,
M/#H#("`#/&UY.E185%]55$8X5&5S=#Y38VARP[QD97(\+VUY.E185%]55$8X
/5&5S=#X*/"]M>5A-3#X*
`
end
Edit 1:
If you want to confirm this theory - run this in eclipse:
#include <iostream>
#include <string>
#include <fstream>
int main()
{
std::ifstream ifs("Test.xml");
std::string xml_data((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
std::cout << xml_data;
}
Output with terminal in UTF-8 mode:
$ ./17676169.cat
<?xml version="1.0" encoding="UTF-8"?>
<myXML>
<my:TXT_UTF8Test>Schrüder</my:TXT_UTF8Test>
</myXML>
Output with terminal in ISO-8859-15 mode:
$ ./17676169.cat
<?xml version="1.0" encoding="UTF-8"?>
<myXML>
<my:TXT_UTF8Test>SchrÃŒder</my:TXT_UTF8Test>
</myXML>

TinyXml to parse conf file

I`m try to realize how to use TinyXML library.
I have to parse this conf file:
<?xml version="1.0" encoding="UTF-8"?>
<Client>
<port num = "20035">
<server_addr ip="127.0.0.1">
<AV_list>
<AV>
<AVNAME>BitDefender</AVNAME>>
<AVPATH> C:\Program Files\Common Files\BitDefender\BitDefender Threat Scanner\av64bit_26308\bdc.exe </AVPATH>
<AVMASK>0x80000000</AVMASK>
<AVCOMMANDLINE> %avpath% \log=%avlog% %scanpath% </AVCOMMANDLINE>
<AVREGEX>(%scanpath%.*?)+(([a-zA-Z0-9]+\\.)+[a-zA-Z]{2,4})+(.+[a-zA-Z_])</AVREGEX>
<AVLOG>C:\log\bd_log.txt</AVLOG>
</AV>
</AV_list>
</Client>
And c++ code
#include "stdafx.h"
#include "iostream"
#include "tinyxml.h"
int main(int argc, char* argv[])
{
TiXmlDocument doc( "D:\\client_conf.xml" );
bool loadOkay = doc.LoadFile();
if ( loadOkay )
printf("Yes \n");
else
printf("No \n");
TiXmlHandle hDoc(&doc);
TiXmlElement* pElem;
TiXmlText* pText;
TiXmlHandle hRoot(0);
pElem = hDoc.FirstChildElement().Element();
if (!pElem)
printf("error element");
hRoot = TiXmlHandle(pElem);
pElem = hRoot.FirstChild("server_addr").Element();
const char* info = pElem->Attribute("ip");
printf( "%s \n", info);
pElem = hRoot.FirstChild("port").Element();
info = pElem->Attribute("num");
printf( "%s \n", info);
system("pause");
return 0;
}
Now I can get first two param, but dont figure out how to reach "AV_list" block. Any help will be appreciated. (:
Have a look at the TinyXml Documentation. Your friend is the TiXmlNode Class Reference. You may use most of the Node functions also on TiXmlElements.
You already use the FirstChild() function to get the first child of an element; use the NextSibling() function to iterate over all elements. You can also use the NextSiblingElement() function to get the element directly.
Antother more sophisticated solution would be to use XPath to retrieve elements from the xml file. There is TinyXPath that builds on top of TinyXML. It needs some knowledge of XPath but it might be worth it. (XPath standard)