![]() |
General Programming »
Algorithms & Recipes »
Parsers
Intermediate
License: The Code Project Open License (CPOL)
XML: Include a Flexible Parser in Your C++ ApplicationsBy Michael ChourdakisFree, portable, compiler-independent XML library in C++ |
C++, XML, Windows, Visual Studio, Dev
|
|
Advanced Search Add to IE Search |
|
|
|
||||||||||||||||
Includes:
Are you tired of the many non-portable XML solutions around? Try my library. It works in any OS and in any compiler. No MFC, no COM, no global variables, Plain, pure C++!
&vars; CDATAFree, for any kind or freeware, shareware, commercial, or whateverware project, as long as you:
STL and exception handling are not available yet in any C++ implementation - for example, no STL exists in Symbian OS SDK. Therefore, I 've decided to use my own Z<> class (which is defined in xml.cpp) in order to manipulate the buffers.
For new code you can define XML_USE_STL before including xml.h to use STL. This results in faster and safer code, but note that it is not 100% compatible with the non STL version, so you will have to make some changes if porting code from the non STL version to the STL one.
The STL version uses std::vector, std::string and standard algorithms.
There are 7 classes:
XML manages the XML file: opens, saves, exports, etc.XMLHeader manages the XML header (XML headers can include comments)XMLComment manages an XML commentXMLContent manages an XML contentXMLElement manages an XML elementXMLVariable manages an XML variableXMLCData manages an XML custom data I won't describe all the member functions of these classes because there is already a description in my help file found in the zip. Here, I will demonstrate simple usage of them. Please note that the library doesn't use exception handling or STL because these two are not always found in C++ implementations (i.e. Symbian).
The code below loads an XML file, then checks for integrity and compresses memory. test.xml is the sample file I used. Note that the element/variable names are case sensitive.
XML* a = new XML("f.xml");
// load from file
XML* a = new XML("<blah f="\">",1);
// load from memory from ASCIIZ string
XML* a = new XML("http://www.some.com/files.xml",2);
// load from URL (Win32)
ASSERT(a->IntegrityCheck() == true && a->ParseStatus() == 0);
a->CompressMemory();
//Get 3rd element's name, and its variable "v" value.
//Also set new variable "tz" with normal and with 'on the fly' mode
char y[100] = {0};
a->GetRootElement()->GetChildren()[0]->GetElementName(y);
// now y == "Cfg"
a->GetRootElement()->GetChildren()[0]->FindVariableZ("v")->GetValue(y);
// now y == "Cfg"
// Create "tz" in the 'normal' mode
XMLElement* e = a->GetRootElement()->GetChildren()[0];
XMLVariable* v = new XMLVariable("tz","some value");
e->AddVariable(v);
// now do not delete v, it is owned by e
// Create "tz" on the fly
a->GetRootElement()->GetChildren()[0]->FindVariableZ(
"tz",true)->SetValue("some value");
// FindVariableZ(x,true) creates the var if doesn't exist!
// Create Comments and Contents in the same way.
// Use 0 instead of y to get the # of bytes required </blah>
// <blah f="\">for the returned string.
// Lets save/export:
if (a->IntegrityTest())
{
a->Save(); // Saves back to file
a->Save("new.xml");
a->Export(fp,1,0,0);
// Save to a fp. You can also export to memory
// or save only one element by calling XMLElement :: Export.
delete a;
// bye bye; </blah>
// <blah f="\">Note that the destructor doesn't save the file by default,
//unless you call XML :: SaveOnClose().
}</blah>
Instead of WritePrivateProfileString, you now have some INI-style functions:
XMLSetStringXMLGetStringXMLSetIntXMLGetIntXMLSetBinaryDataXMLGetBinaryDataXMLSetFloatXMLGetFloatThese functions can work standalone or with an already opened XML object. When a valid XML object is passed to them, they modify it. When a file name is passed to them, they create an XML object, load the file, read/write it and then save it back, pretty much as the INI file functions.
Because these functions accept their elements as a string with \ (for example, Cfg\\Amplify), you can only manipulate XML files that have unique element names. Otherwise, corruption will occur.
Now let's try getting/setting some data to our XML file:
char y[1000] = {0};
XMLGetString("Cfg","v","",y,1000,"test.xml");
// This gets 'Bowlingy' to y.
XMLGetString("Cfg\\Amplify","V","not_found",y,1000,"test.xml");
// This gets 'not_found' to y. Variables/Elements are case sensitive!
XMLSetString("A\\B\\C","v","hahaha","test.xml");
// Elements A B C are created!
Fread(y,1,100,some_file);
XMLSetBinaryData("A\\B\\C","v",y,100,"test.xml");
// Binary data can be saved/read, encoded with Base64 (mime.h)
Note that all these string functions require a UTF-8 string. In case you are under Windows, you can also call XMLSetString with wchar_t values, which the library automatically converts to UTF-8 by using WideCharToMultiByte.
Use XMLElement :: UnloadElement, ReloadElement to temporarily save an element to a memory file (check the help file for details). This enables you to manipulate a huge XML file without wasting your RAM.
Soon to implement XML :: PartialLoad().
Use XMLElement :: BorrowItem to add a XMLElement* mirror from another XMLElement*. This is an advanced feature that must be used with caution, or you will crash your application with a big nice stack overflow. Read the help file for details.
A temporal element is an XMLElement (or an XMLVariable) that simply has the 'temporal' flag on. You can set or query this flag by calling SetTemporal and GetTemporal member functions. By default, elements and variables are not temporal.
You can also create a temporal element or variable by a constructor flag. Also, XMLElement :: FindElementZ() and XMLElement :: FindVariableZ, which can create an element/variable on the fly, can also mark it as temporal.
When you call XMLElement :: RemoveTemporalElements(bool Deep) , all temporal children elements are removed. If Deep is true, all temporal elements of all its children are also removed. If an element is temporal and it is removed, then all children of it are of course removed, even if not marked as temporal. Therefore, marking, say, your root element as temporal will result in the destroyment of the entire XML file when you call RemoveTemporalElements().
When you call XMLElement :: RemoveTemporalVariables(bool Deep), all temporal variables of this element are removed. If Deep is set to true, all temporal variables of all its children are also removed.
When you call XML :: RemoveTemporalElements(), it calls XMLElement::RemoveTemporalElements(true) and XMLElement::RemoveTemporalVariables(true) for the root element.
Note that unless you remove the temporal elements manually using the above functions, they are not removed - they are considered normal elements/variables and they are saved or exported normally.
The library works with UTF-8, which means that the strings passed and returned are char*s. In order to pass a Unicode string to the library, you must convert it manually WideCharToMultiByte(CP_UTF8,...);. I have created a very simple wrapper that can do that as a class:
class W
{
public:
W(const wchar_t* x)
{
int y = wcslen(x);
int wy = y*2 + 100;
we = new char[wy];
memset(we,0,wy);
WideCharToMultiByte(CP_UTF8,0,x,-1,we,wy,0,0);
}
~W()
{
delete[] we;
}
operator char* ()
{
return we;
}
};
So if you have a Unicode string x and you want to use it in my library, you would use W(x) instead, which converts your Unicode string to a UTF-8 string, uses it via the operator and the destructor frees it.
The library is 64-bit compatible; you will be able to compile and use it under any 64-bit compiler without problems.
You will at times need to update a XMLElement with the elements and variables of another element.
int UpdateElement(XMLElement* NewEl,bool UpdateVariableValues = false);
This will:
NewEl. If any of them also exists in your current element, then if UpdateVariableValues == true, the function will update the variable. If the variable of NewEl does not exist into your element, it is copied.NewEl. If any of the elements do not exist in your current element, it is copied. If it exists, then the function calls UpdateElement for that element against the child element of NewEl, resulting in a recursive update of all grand children and variables.Use XML :: ImportDB() to import a database. You have to mess with two structures:
struct IMPORTDBTABLEDATA
{
char name[256];
char itemname[100];
int nVariables;
char** Variables;
char** ReplaceVariables;
};
struct IMPORTDBPARAMS
{
char* dbname;
char* provstr;
int nTables;
IMPORTDBTABLEDATA* Tables;
};
You fill the IMPORTTDBPARAMS with the name (optional) , the provider string (check ADO documentation for details), the number of tables you wish to import, and then you fill an IMPORTDBTABLEDATA structure for each of these tables - containing the table name, the XMLElement* item name to store, the number of variables to take (columns from the table) , and a double pointer to the variable names and the variable names to be stored. For an example, see xmltest.cpp.
You can use XMLElement :: XMLQuery() which accepts an expression to test, the deep to search (-1 if all the child elements are to be queried), and a pointer to the returned XMLElement*s (which are not duplicated; the function just copies their pointers to your array). An example below:
XMLElement* e = ... ;// get this from somewhere
int nC = e->GetAllChildrenNum();
XMLElement* a = new XMLElement*[nC];
memset(a,0,sizeof(XMLElement*)*nC);
int nR = e->XMLQuery("some_var == \"*5*\"",a,-1);
for(int i = 0 ; i < nR ; i++)
{
... // use a
}
delete[] a;
The above code returns pointers to all children elements that have a variable with the name 'some_var' which has a '5' inside (regular pattern expressions are supported). For more, see XML.CHM.
These are some other current features:
XML :: ImportDB() to import a database (every ADO database supported) to an XML element (examples are in the help file) Use XML :: Query() to query the database These are some features I'd like to implement in the future:
Please leave your questions and comments!
XMLTest: A command line demo to discover the mains of the library TXML: The full database/XML file solution for Win32: Exploits all the power of the library. Features:
XMLPPC: The lite XML editor for Windows Mobile 5+. Supports every feature as TXML, except multiple files opening and database queries.XMLPPSavemode" "Loadmode" and "Targetmode" to readable enumsint' s with 'size_t' allowing further expansion to X64 and eliminating C4267Const" to more functions< and >const to some functionsSetValueInt64 and GetValueInt64SetValueInt to use %i instead of %uXML :: SetUnicode()Item param to be 64-bitsZ<>XMLElement :: MoveElementXMLElement :: UpdateElementXML :: XML, XML :: Load() and XML :: Save() accept Unicode filenamesXMLDialog()XMLQuery bug fixedXMLElement :: AddElement, AddVariable, AddComment, AddContent , AddCDataCData bug fixed CData bug fixed XMLCData addedXML::XMLQuery() updatedAddBlankVariable bug fixedXML:: ImportDB() fix
General
News
Question
Answer
Joke
Rant
Admin
|
PermaLink |
Privacy |
Terms of Use
Last Updated: 18 Aug 2009 Editor: Sean Ewington |
Copyright 2007 by Michael Chourdakis Everything else Copyright © CodeProject, 1999-2009 Web22 | Advertise on the Code Project |