Introduction
I was looking for a lightweight and easy to use XML Serialization library for C++.
Mainly for simple tasks like to store application settings or to send messages.
After looking around I found a few candidates. But most of them are too complicated or to heavy.
I thought there must be a simple solution, without defining a bulk of interfaces or helper classes.
How it works
Creating serializable classes is pretty simple and straightforward.
- Derive you class from "Serializeable"
- Add members
Members can be basic types like xString, xInt, xBool
or complex types like member classes or collections of member classes. - Register members in the constructor
Features
- Serialization / Deserialization
- Member-Classes
- Member-Collections
- Copy / Clone objects
- Compare objects
- Find / Replace values
- Complete code in 1 header and 1 code file (+tinyxml2)
Using the code
Let's have a look at the example "application settings":
The first step is to define your class containing all needed setting values.
As you can see, serializeable members are defined as xString, xInt, xBool...
All other types are allowed, of course. They are simply ignored during the serialization process.
class ApplicationSettings: public Serializable
{
public:
ApplicationSettings();
xString Setting1;
xString Setting2;
xString Setting3;
xInt Setting4;
xInt Setting5;
xBool Setting6;
xTime_t Setting7;
};
The second step is to register all x-members for serialization.
This is usually done in the constructor:
ApplicationSettings::ApplicationSettings():
Setting1("Default string value"), Setting4(1234)
{
setClassName("ApplicationSettings");
setVersion("2.1");
Register("Setting1", &Setting1);
Register("Setting2", &Setting2);
Register("Setting3", &Setting3);
Register("Setting4", &Setting4);
Register("Setting5", &Setting5);
Register("Setting6", &Setting6);
Register("Setting7", &Setting7);
};
Set and access member values in your code:
ApplicationSettings *settings=new ApplicationSettings; settings->Setting1="Settings string 1"; settings->Setting2="Settings string 2";
settings->Setting3="Settings string 3";
settings->Setting4=1234;
settings->Setting5=5678;
settings->Setting6=false;
settings->Setting7=true;
std::string strValue = settings->Setting1.value();
int intValue = settings->Setting4.value();
Serialize the class to string:
std::string xmlData = settings->toXML();
XML - Result:
<SerializableClass Type="ApplicationSettings" Version="2.1">
<Member Name="Setting1">Settings string 1</Member>
<Member Name="Setting2">Settings string 2</Member>
<Member Name="Setting3">Settings string 3</Member>
<Member Name="Setting4">1234</Member>
<Member Name="Setting5">5678</Member>
<Member Name="Setting6">false</Member>
<Member Name="Setting7">true</Member>
</SerializableClass>
Deserialize from string:
ApplicationSettings* dser_Settings=new ApplicationSettings; if (Serializable::fromXML(xmlData, dser_Settings) {
cout << dser_Settings->Setting1.value() << endl; cout << dser_Settings->Setting1.toString() << endl; }
Member Classes and Collections
Additionally we want to store database login and a list of used document items:
class LastUsedDocument: public Serializable
{
public:
LastUsedDocument();
xString Name;
xString Path;
xInt Size;
};
class DatabaseLogin: public Serializable
{
public:
DatabaseLogin();
xString HostName;
xInt Port;
xString User;
xString Password;
};
LastUsedDocument::LastUsedDocument()
{
setClassName("LastUsedDocument");
Register("Name", &Name);
Register("Path", &Path);
Register("Size", &Size);
};
DatabaseLogin::DatabaseLogin()
{
setClassName("DatabaseLogin");
Register("HostName", &HostName);
Register("Port", &Port);
Register("User", &User);
Register("Password", &Password);
};
The updated ApplicationSettings class definition including document collection and login data.
class ApplicationSettings: public Serializable
{
public:
ApplicationSettings();
xString Setting1;
xString Setting2;
xString Setting3;
xInt Setting4;
xInt Setting5;
xBool Setting6;
xBool Setting7;
DatabaseLogin Login; Collection<LastUsedDocument> LastUsedDocuments; };
ApplicationSettings::ApplicationSettings()
{
...
Register("Setting7", &Setting7);
Register("Login", &Login); Register("LastUsedDocuments", &LastUsedDocuments); }
Using member classes and collections:
cout << "Login, URL:" << endl;
cout << "Hostname: " << settings->Login.HostName.value();
cout << ":" << settings->Login.Port.value() << endl;
cout << "Show all collection items" << endl;
for (size_t i=0; i<settings->LastUsedDocuments.size(); i++)
{
LastUsedDocument* doc = settings->LastUsedDocuments.getItem(i);
cout << "Item " << i << ": " << doc->Name.value() << endl;
}
Final XML:
<SerializableClass Type="ApplicationSettings" Version="2.1">
<Member Name="Setting1">Settings string 1</Member>
<Member Name="Setting2">Settings string 2</Member>
<Member Name="Setting3">Settings string 3</Member>
<Member Name="Setting4">1234</Member>
<Member Name="Setting5">5678</Member>
<Member Name="Setting6">false</Member>
<Member Name="Setting7">true</Member>
<Class Name="Login" Type="DatabaseLogin" Version="1">
<Member Name="HostName">my.db.Settings.server.local</Member>
<Member Name="Port">2000</Member>
<Member Name="User">john.smith</Member>
<Member Name="Password">newPassword</Member>
</Class>
<Collection Name="LastUsedDocuments">
<Class Type="LastUsedDocument" Version="1">
<Member Name="Name">Document #1</Member>
<Member Name="Path">c:\temp\</Member>
<Member Name="Size"></Member>
</Class>
<Class Type="LastUsedDocument" Version="1">
<Member Name="Name">Document #2</Member>
<Member Name="Path">c:\temp\</Member>
<Member Name="Size"></Member>
</Class>
<Class Type="LastUsedDocument" Version="1">
<Member Name="Name">Document #3</Member>
<Member Name="Path">c:\temp\</Member>
<Member Name="Size"></Member>
</Class>
<Class Type="LastUsedDocument" Version="1">
<Member Name="Name">Document #4</Member>
<Member Name="Path">c:\temp\</Member>
<Member Name="Size"></Member>
</Class>
<Class Type="LastUsedDocument" Version="1">
<Member Name="Name">Document #5</Member>
<Member Name="Path">c:\temp\</Member>
<Member Name="Size"></Member>
</Class>
</Collection>
</SerializableClass>
Tools
Compare objects
if (object_1->Compare(object_2))
cout << "equal" << endl; else
cout << "net equal" << endl;
Clone/Copy objects
ApplicationSettings *destination=new ApplicationSettings;
Serializable::Clone(source, destination);
Find/Replace
settings->Replace("{FILEPATH}", "c:\\temp\\");
Other frameworks
If you need a complete XML framework, have a look at Brian Aberle's XMLFoundation.
Nice Article!
History
Version 1.0: Initial publication.