I am having a lot of trouble working with the libxml2 library to parse an xml file.
I have weeded out a previous, similar problem, but have run into another.
Here is the problem code:
class SSystem{
public:
//Constructors
SSystem(){};
//Make SSystem from XML Definition. Pass ptr to node
SSystem(xmlNodePtr Nptr, xmlDocPtr Dptr){
name = wxString((char *)xmlGetProp(Nptr, (xmlChar*)"name"), wxConvUTF8);
//Move to next level down, the <general> element
Nptr = Nptr->xmlChildrenNode;
//Move one more level down to the <radius> element
Nptr = Nptr->xmlChildrenNode;
//Get Radius value
if (!xmlStrcmp(Nptr->name, (const xmlChar *)"radius")) {
char* contents = (char*)xmlNodeGetContent(Nptr);
std::string test1 = std::string(contents);
radius = wxString(contents, wxConvUTF8);
}
}
Both an xmlNodePtr and an xmlDocPtr are passed to the constructor, which works fine taking just a property ("name"), but is now choking on further parsing.
Here is a piece of the xml file in question:
<?xml version="1.0" encoding="UTF-8"?>
<Systems>
<ssys name="Acheron">
<general>
<radius>3500.000000</radius> <-- I am trying to get this value (3500).
<stars>300</stars>
<asteroids>0</asteroids>
<interference>0.000000</interference>
<nebula volatility="0.000000">0.000000</nebula>
</general>
It compiles fine, but crashes when the constructor is loaded (I know because, if I comment out the if conditional and the char* contents = (char*)xmlNodeGetContent(Nptr->xmlChildrenNode), it runs fine.
I've tried so many different things (removed one of the Nptr->xmlChildrenNode), but nothing works.
What is wrong?
This:
char* contents = (char*)xmlNodeGetContent(Nptr->xmlChildrenNode)
Should probably be this:
char* contents = (char*)xmlNodeGetContent(Nptr)
Okay, I am going to use a different XML parsing library, as Libxml is a bit too complicated for me.
I am looking into using MiniXML (http://www.minixml.org/).
#Biosci3c:
The method you are calling returns some fake value. You should not call the method
char*)xmlNodeGetContent(Nptr->xmlChildrenNode)
instead you have to get the data corresponding to radius in cdata callback method below here.
void cdataBlock (void * ctx,
const xmlChar * value,
int len)
Check out in libxml library documentation for reference...
I just wrote a C++ wrapper to libxml2. It is on github if someone is interested: https://github.com/filipenf/libxml-cpp-wrapper
The idea is to make the use of libxml2 easier for C++ programmers - that's the main goal of this wrapper.
In the github repository there is a simple example of how to use it, but you can use it like this:
string office_phone = reader.getNodes()[0]["Customer"]["ContactInfo"]["OfficePhone"].text;
It is a work-in-progress so there is many room for improvement....
Related
I was wondering how could I update the data on the DOM for a certain attribute? I've searched but I couldn't find anything. Basically, I have an attribute called Hour(for example it's "11:03") and I want the text from that specific attribute to be changed to something like "11:04" or any other different text.
if( strcmp(Code1,Code2) == 0 )
{
strcpy(New,NewHour);
Element->FindAttribute("Hour")->SetAttribute(New); // here I want it to be changed in the DOM but I dont know how to do it
}
Later edit: This is what I've tried, but it's telling me FindAttribute() is private..
It is true that you can use SetAttribute which accepts the attribute name and value as parameters.
However, TinyXml2 does have a methodology for using FindAttribute because I have this code in my application:
// We need to get the assistant
const XMLAttribute *pAttrAssistant = const_cast<const XMLElement*>(pStudent)->FindAttribute("Assistant");
if (pAttrAssistant != nullptr)
{
LPCTSTR szAssistant = CA2CT(pAttrAssistant->Value(), CP_UTF8);
SetStudentInfo(eSchool, eAssign, strStudent, szAssistant, iStudyPoint);
}
else
{
// TODO: Throw exception if Assistant attribute missing
}
As you can see, I use the FindAttribute method and I have no compilation errors. If you look closely you will see that I am using const and that is the key.
The class exposes two methods:
One of them is set to private as you have already found out. But the const overload is set as public:
I'm trying to use a COM object and i'm having problem with the parameter type VARIANT*. I can use the functions of the COM object just fine, except when they have a parameter of this type.
The doc generated by generateDocumentation is :
QVariantList params = ...
object->dynamicCall("GetRanges(int,int,int&, QVariant&)", params);
According to the doc provided with the COM object, the parameters should be of type LONG, LONG, LONG* and VARIANT*, and it is precised that the VARIANT* is a pointer to a VARIANT containing an array of BSTR.
I should normally be able to retrieve the third and fourth parameter (of type LONG* and VARIANT*), and their values are not used by the function.
Here is my code (a and b are int previously initialized):
QStringList sl;
QVariantList params;
int i = -1;
params << QVariant (a);
params << QVariant (b);
params << QVariant (i);
params << QVariant (sl);
comobject->dynamicCall("GetRanges(int,int,int&,QVariant&)",params);
sl = params[3].toStringList();
i = param[2].toInt();
Now with that code, all i get is an error QAxBase: Error calling IDispatch member GetRanges: Unknown error, which is not very helpful.
I tried to change some things and I managed to progress (sort of) by using this code :
QStringList sl;
QVariant v = qVariantFromValue(sl);
QVariantList params;
int i = -1;
params << QVariant (a);
params << QVariant (b);
params << QVariant (i);
params << qVariantFromValue((void*)&v);
comobject->dynamicCall("GetRanges(int,int,int&,QVariant&)",params);
sl = params[3].toStringList();
i = param[2].toInt();
It gets rid of the error, and the value of i is correct at the end, but sl is still empty. And I know it should not be, because I have a sample demo in C# that works correctly.
So if anyone has an idea on how to make it works...
Otherwise I looked around a bit and saw that it was also possible to query the interface ans use it directly, but I didn't understand much, and I'm not sure it will solve my problems.
I'm on a Windows7 64 bits platform, and I'm using msvc2012 as compiler. I'm using Qt 5.1.0 right now, but it didn't work in the 5.0.2 either.
I guess you really can't do it with dynamicCall.
I finally found how to do it. It was easier than I'd thought. With the installation of Qt comes a tool called dumpcpp. Its full path for me was C:\Qt\Qt5.1.0x86\5.1.0\msvc2012\bin\dumpcpp.exe (obviously depends on settings). You can just add the bin folder to your path to make it easier to use.
Then I went into my project folder and executed this command :
dumpcpp -nometaobject {00062FFF-0000-0000-C000-000000000046} (the CLSID is just for the example, not the one I used)
It creates a header file, you can include it in the file where you're trying to use the COM Object.
In this file in my case there was two classes (IClassMeasurement and ClassMeasurement) in a namespace (MeasurementLib). Again, the names are not the real ones.
In your initial project file, you can call the desired function like this :
MeasurementLib::ClassMeasurement test; //Do not use IClassMeasurement, you only get write access violations
QVariant rangesVar;
int p1 = 0;
int p2 = 0;
int p3 = 0;
test.getRanges(p1,p2,p3,ranges);
QStringList ranges = ranges.toStringList();
Hopes that it helps someone !
I have a rather large code for the data analysis software Root (CERN) and I have a run of data that I want to look through for bad runs. I have them all in one directory, but want to write a segment of code to take one file out of this folder at a time, run the code, output the resulting graphs, then take the next file.. etc. I am using a macro to run this code as it is now. I am hoping to just add something to that macro. I am somewhat novice to programming.
gSystem->Load("AlgoCompSelector_C.so");
// make the chains
std::string filekey;
TChain tree1 = new TChain("tree");
filekey = std::string("data/run715604.EEmcTree_Part1.root");
tree1->Add( filekey.data() );
To do this in a single root macro, you can try something like the code snippet below. here I add the files to a TChain but you could of course replace the TChain::Add with whatever you want.
int addfiles(TChain *ch, const char *dirname=".", const char *ext=".root")
{
int added = 0;
TSystemDirectory dir(dirname, dirname);
TList *files = dir.GetListOfFiles();
if (files) {
TSystemFile *file;
TString fname;
TIter next(files);
while ((file=(TSystemFile*)next())) {
fname = file->GetName();
if (!file->IsDirectory() && fname.EndsWith(ext)) {
ch->Add(fname); // or call your function on this one file
++added;
}
}
}
return added;
}
(Adapted from this root-talk post: http://root.cern.ch/phpBB3/viewtopic.php?f=3&t=13666)
Having said that I think the suggestion by #m0skit0 to launch a smaller script each time is a better one than doing what you propose to do above. Root is finicky and having smaller jobs is better.
I'm currently working on a project in C++ where I need to read some things from a xml file, I've figured out that tinyxml seams to be the way to go, but I still don't know exactly how to do.
Also my xml file is a little tricky, because it looks a little different for every user that needs to use this.
The xml file I need to read looks like this
<?xml version="1.0" encoding="utf-8"?>
<cloud_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx xmlns:d="http://www.kuju.com/TnT/2003/Delta" d:version="1.0">
<cCareerModel d:id="154964152">
<ScenarioCareer>
<cScenarioCareer d:id="237116344">
<IsCompleted d:type="cDeltaString">CompletedSuccessfully</IsCompleted>
<BestScore d:type="sInt32">0</BestScore>
<LastScore d:type="sInt32">0</LastScore>
<ID>
<cGUID>
<UUID>
<e d:type="sUInt64">5034713268864262327</e>
<e d:type="sUInt64">2399721711294842250</e>
</UUID>
<DevString d:type="cDeltaString">0099a0b7-e50b-45de-8a85-85a12e864d21</DevString>
</cGUID>
</ID>
</cScenarioCareer>
</ScenarioCareer>
<MD5 d:type="cDeltaString"></MD5>
</cCareerModel>
</cloud_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx>
Now the goal of this program is to be able to insert some string (via. a variable) and serch for the corresponding "cScenarioCarrer d:id" and read the "IsComplete" and the "BestScore".
Those strings later need to be worked with in my program, but that I can handle.
My questions here are
A. How do I go by searching for a specific "cScenarioCareer" ID
B. How do I paste the "IsComplete" and "BestScore" into some variables in my program.
Note: The xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx string is unique for every user, so keep in mind it can be anything.
If anyone out there would like to help me, I'd be very graceful, thank you.
PS. I'd like to have some kind of understanding for what I'm doing here, all though "paste this code into your program" answers are acceptable, I think it would be much better if you can tell me how and why it works.
Since you're doing this in C++ I'll make this example using the ticpp interface to
TinyXml that available at ticpp.googlecode.com.
Assumptions:
A given xml file will contain one <cloud> tag and multiple
<cCareerModel> tags.
Each <cCareerModel> contains a single <ScenarioCareer> tag which in turn contains a single <cScenarioCareer> tag
You've parsed the xml file into a TiXmlDocument called xmlDoc
You don't need to examine the data type attributes
You don't mind using exceptions
I'll also assume that you have a context variable somewhere containing a pointer to the
<cloud> tag, like so:
ticpp::Element* cloud = xmlDoc.FirstChildElement("cloud");
Here's a function that will locate the ticpp::Element for the cScenarioCareer with
the given ID.
ticpp::Element* findScenarioCareer(const std::string& careerId)
{
try
{
// Declare an iterator to access all of the cCareerModel tags and construct an
// end iterator to terminate the loop
ticpp::Iterator<ticpp::Element> careerModel;
const ticpp::Iterator<ticpp::Element> modelEnd = careerModel.end();
// Loop over the careerModel tags
for (careerModel = cloud->FirstChildElement() ; careerModel != modelEnd ;
++careerModel)
{
// Construct loop controls to access careers
ticpp::Iterator<ticpp::Element> career;
const ticpp::Iterator<ticpp::ELement> careerEnd = career.end();
// Loop over careers
for (career = careerModel->FirstChildElement("ScenarioCareer").FirstChildElement() ;
career != careerEnd ; ++career)
{
// If the the d:id attribute value matches then we're done
if (career->GetAttributeOrDefault("d:id", "") == careerId)
return career;
}
}
}
catch (const ticpp::Exception&)
{
}
return 0;
}
Then to get at the information you want you'd do something like:
std::string careerId = "237116344";
std::string completion;
std::string score;
ticpp::Element* career = findScenarioCareer(careerId);
if (career)
{
try
{
completion = career->FirstChildElement("IsCompleted")->GetText();
score = career->FirstChildElement("BestScore")->GetText();
}
catch (const ticpp::Exception&)
{
// Handle missing element condition
}
}
else
{
// Not found
}
Naturally I haven't compiled or tested any of this, but it should give you the idea.
I am using the directory class to get this information but unable to assign this data to a data member of my own class. i am doing an oop project. Furthermore,I want to use the concept of Dynamism(containment).I have created two class, mydirectory and myfiles as under:
class files
{
string fname[25];
public:
files()
{
fname=NULL;
}
};
class directory
{ private:
directory *d;
string *dname[25]; //* to show there may be a subdirectory,there may be not.
files *ff[25]; // data member string *fname[25];
int numd,numf;
public:
directory()
{
numd=0;numf=0;
}
Now when if I want to use the statment:
Directory::GetDirectories("D:\\");
how can I assign the directory names to "dname" of directory class.
I dont want to include a third party software.
also i need help on the topic: how can a file (doc file/pdf/txt/pwt etc) can be opened from c++ code outside the console? I am very worried. please help me. thanks in advance.
I am new to c++ so please forgive if there are any errors in pointer handling, as I am doing this containment for the first time. I also need some reading stuff.
The simplest way to do it in C++ is using boost::filesystem.
As long as the path is a directory you can iterate over it using either a directory_iterator or a recursive_directory_iterator.
eg:
boost::filesystem::path dirname( "D:\\" );
std::vector<boost::filesystem::path> topLevel( directory_iterator(dirName),
directory_iterator() );
std::vector<boost::filesystem::path> wholeDrive(
recursive_directory_iterator(dirName), recursive_directory_iterator() );
As this is marked homework, we're not going to be helping you much by giving you the correct answer. But I will point you in the right direction.
You've indicated you're doing this under Visual C++. Well without using any third party libraries but just what's built in, you'll need to access the Win32 API.
FindFirstFile() & FindNextFile() are what you need.
You'll call FindFirstFile first off to obtain the directory handle.
The parameter is the D:\ that you're passing into your class.
Then call FindNextFile in a while loop.
e.g. The basic principle of using those API's is
HANDLE h = FindFirstFile("D:\\");
WIN32_FIND_DATA data;
while (FindNextFile(h, &data))
{
// Check if it's a directory or not
if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY){
// Add to dname
}
}
Consider using std::vector for your dname instead of string*
because you're stuck with 25 entries. Using vector it'll grow for you.
As said CashCow, boost::filesystem
As a general rule, in C++ for such examples, you don't need any pointer. Here a some mistakes you should correct:
string fname[25];
This declares an array of 25 strings. You probably wanted a string of 25 chars ? Well, in std::string, you don't need to care about the length. std::string fname; is enough
std::string file_name;
file_name = "baz.txt";
fname=NULL;
If fname is a string, then it's not a pointer. So you can't assign NULL to it. A std::string is by default initialized as an empty string. You can leave the whole constructor out.
string *dname[25]
I suppose you wanted to have an array of string. Just use :
std::vector<std::string> dnames;
dnames.push_back("foo");
dnames.push_back("bar"); // dnames now contains {"foo","bar"}
And you'll have a dynamically resizable vector of strings.
See : no need of any pointer. No need for any new
Finally I completed the short project.To get the list of files and sub directories, I made use of .NET Framework namespace "System".It has got classes like "FileInfo" and "DirectoryInfo"(both belong to System::IO) which do the above required task.But here,all the string related stuff is of System::String , not of std::string.To convert System::String to std::string, I used the following code(I got this conversion's code from a forum and it worked fine without any error):
string Str2str(String ^a)
{
array<Byte> ^chars = System::Text::Encoding::ASCII->GetBytes(a);
pin_ptr<Byte> charsPointer = &(chars[0]);
char *nativeCharsPointer = reinterpret_cast<char *>(static_cast<unsigned char *>(charsPointer));
string native(nativeCharsPointer, chars->Length);
return native;
}
Here is a short code for getting list of sub directories from a drive(D: drive is going to be searched):
#include<iostream>
#using<mscorlib.dll>
using namespace strd;
using namespace System;
using namespace System::IO;
int main()
{int count=50;
string name[count];//to hold directories names
string b;
int s=0;
DirectoryInfo^ di = gcnew DirectoryInfo("d:\\");
if(di->Exists)
{array<DirectoryInfo^>^diArr = di->GetDirectories();
Collections::IEnumerator^ myEnum = diArr->GetEnumerator();
while ( myEnum->MoveNext() )
{DirectoryInfo^ dri = safe_cast<DirectoryInfo^>(myEnum->Current);
String ^a=(dri->Name->ToString());
int n=b.size();
b=Str2str(a); `// code given in the starting`
if (s<count)
{name[s]=b;
s++;}
}
This involves Managed C++ knowledge. Visit these:
.NET Programming Guide
C++: The Most Powerful Language for .NET Framework Programming
I compiled this on Visual Studio 2008. I will be very grateful if you appriciate my effort.Further suggestions are most welcomed.