C++ class for reading and writing XML structures. No need for a C++ code parser or special pre compiler. Specify a schema entirly in native C++. The schema is verified when XML is read and exceptions are thrown when the XML to be parse is invalid.
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
358 lines
13 KiB
358 lines
13 KiB
/*! @file |
|
|
|
@id $Id$ |
|
*/ |
|
// 1 2 3 4 5 6 7 8 |
|
// 45678901234567890123456789012345678901234567890123456789012345678901234567890 |
|
|
|
#include <xml-cxx/xml.hxx> |
|
#include <cppunit/TestFixture.h> |
|
#include <cppunit/ui/text/TestRunner.h> |
|
#include <cppunit/extensions/HelperMacros.h> |
|
#include <cppunit/extensions/TestFactoryRegistry.h> |
|
#include <cppunit/XmlOutputter.h> |
|
#include <fstream> |
|
|
|
class A: public xml::Serialize { |
|
public: |
|
int _anInteger; |
|
bool _aBool; |
|
double _aDouble; |
|
std::string _aString; |
|
std::string _anotherString; |
|
unsigned long _aLong; |
|
protected: |
|
void initXmlMembers() { |
|
className("A"); |
|
persist(_anInteger, "anInteger"); |
|
persist(_aBool, "aBool"); |
|
persist(_aDouble, "aDouble"); |
|
persist(_aString, "aString"); |
|
persist(_anotherString, "anotherString"); |
|
persist(_aLong, "aLong"); |
|
} |
|
}; |
|
|
|
class B: public xml::Serialize { |
|
public: |
|
long aLong; |
|
char aChar; |
|
protected: |
|
void initXmlMembers() { |
|
className("B"); |
|
persist(aLong, "aLong"); |
|
persist(aChar, "aChar"); |
|
} |
|
}; |
|
|
|
class B2: public B { |
|
public: |
|
float aFloat; |
|
short aShort; |
|
protected: |
|
void initXmlMembers() { |
|
B::initXmlMembers(); |
|
className("B2"); |
|
persist(aFloat, "float"); |
|
persist(aShort, "short"); |
|
} |
|
}; |
|
|
|
// Complex Example: Class A2 is a class that inherits another class |
|
// (A) and contains a class (B2) that inherits another class (B) |
|
class A2: public A { |
|
public: |
|
std::string aText; |
|
B2 childOfB; |
|
protected: |
|
void initXmlMembers() { |
|
A::initXmlMembers(); |
|
className("A2"); |
|
persist(aText, "aText"); |
|
persist(childOfB, "childOfB"); |
|
} |
|
}; |
|
|
|
class C: public xml::Serialize { |
|
public: |
|
A2 a2; |
|
B2 b2; |
|
protected: |
|
void initXmlMembers() { |
|
className("C") |
|
.persist(a2, "a2") |
|
.persist(b2, "b2"); |
|
} |
|
}; |
|
|
|
class SerializationTest: public CppUnit::TestFixture { |
|
public: |
|
std::string _file; |
|
A _a; |
|
void setUp() { |
|
_a._anInteger = 15; |
|
_a._aBool = true; |
|
_a._aDouble = 123.456; |
|
_a._aString = "Hello World"; |
|
_a._anotherString = "This is another Text"; |
|
_a._aLong = 4123674622ul; |
|
try { |
|
std::stringstream ss; |
|
_a.saveXml(ss); |
|
_file = ss.str(); |
|
} catch (...) {} |
|
} |
|
void memberDeclaration() { |
|
// This is more a compile time test than a runtime test. |
|
A a; |
|
a._anInteger = 15; |
|
CPPUNIT_ASSERT_EQUAL(15, a._anInteger); |
|
a._aString = "Hello World"; |
|
CPPUNIT_ASSERT_EQUAL(std::string("Hello World"), a._aString); |
|
std::stringstream ss; |
|
} |
|
void store() { |
|
{ |
|
std::stringstream ss; |
|
CPPUNIT_ASSERT_NO_THROW(_a.saveXml(ss)); |
|
CPPUNIT_ASSERT_EQUAL(std::string("<A>\n" |
|
"\t<anInteger>15</anInteger>\n" |
|
"\t<aBool>true</aBool>\n" |
|
"\t<aDouble>123.456</aDouble>\n" |
|
"\t<aString>Hello World</aString>\n" |
|
"\t<anotherString>This is" |
|
" another Text</anotherString>\n" |
|
"\t<aLong>4123674622</aLong>\n" |
|
"</A>"), |
|
ss.str()); |
|
} { // again - initialisation of A should be done only once |
|
std::stringstream ss; |
|
CPPUNIT_ASSERT_NO_THROW(_a.saveXml(ss)); |
|
CPPUNIT_ASSERT_EQUAL(_file, ss.str()); |
|
} |
|
} |
|
void restore() { |
|
A a; |
|
std::stringstream ss(_file); |
|
CPPUNIT_ASSERT(_file.size()>0); |
|
CPPUNIT_ASSERT_EQUAL(_file, ss.str()); |
|
CPPUNIT_ASSERT_NO_THROW(a.loadXml(ss)); |
|
CPPUNIT_ASSERT_EQUAL(15, a._anInteger); |
|
CPPUNIT_ASSERT_EQUAL(true, a._aBool); |
|
CPPUNIT_ASSERT_EQUAL(123.456, a._aDouble); |
|
CPPUNIT_ASSERT_EQUAL(std::string("Hello World"), a._aString); |
|
CPPUNIT_ASSERT_EQUAL(std::string("This is another Text"), |
|
a._anotherString); |
|
CPPUNIT_ASSERT_EQUAL(4123674622ul, a._aLong); |
|
} |
|
void complexLoad() { |
|
std::stringstream ss("<A2>\n" |
|
" <anInteger>-1234</anInteger>\n" |
|
" <aBool>true</aBool>\n" |
|
" <aDouble>3.141</aDouble>\n" |
|
" <aString>This is A inside of A2</aString>\n" |
|
" <anotherString>Another A-String</anotherString>\n" |
|
" <aLong>1234567890</aLong>\n" |
|
" <aText>Text from A2</aText>\n" |
|
" <childOfB>\n" |
|
" <aLong>987654321</aLong>\n" |
|
" <aChar>Q</aChar>\n" |
|
" <float>2.5</float>\n" |
|
" <short>-127</short>\n" |
|
" </childOfB>\n" |
|
"</A2>"); |
|
A2 a2; |
|
CPPUNIT_ASSERT_EQUAL(std::string("<A2>\n" |
|
"\t<anInteger/>\n" |
|
"\t<aBool/>\n" |
|
"\t<aDouble/>\n" |
|
"\t<aString/>\n" |
|
"\t<anotherString/>\n" |
|
"\t<aLong/>\n" |
|
"\t<aText/>\n" |
|
"\t<childOfB>\n" |
|
"\t\t<aLong/>\n" |
|
"\t\t<aChar/>\n" |
|
"\t\t<float/>\n" |
|
"\t\t<short/>\n" |
|
"\t</childOfB>\n" |
|
"</A2>"), a2.schema()); |
|
CPPUNIT_ASSERT_NO_THROW(a2.loadXml(ss)); |
|
CPPUNIT_ASSERT_EQUAL(-1234, a2._anInteger); |
|
CPPUNIT_ASSERT_EQUAL(true, a2._aBool); |
|
CPPUNIT_ASSERT_EQUAL(3.141, a2._aDouble); |
|
CPPUNIT_ASSERT_EQUAL(std::string("This is A inside of A2"), a2._aString); |
|
CPPUNIT_ASSERT_EQUAL(std::string("Another A-String"), a2._anotherString); |
|
CPPUNIT_ASSERT_EQUAL(1234567890ul, a2._aLong); |
|
CPPUNIT_ASSERT_EQUAL(std::string("Text from A2"), a2.aText); |
|
CPPUNIT_ASSERT_EQUAL(987654321l, a2.childOfB.aLong); |
|
CPPUNIT_ASSERT_EQUAL('Q', a2.childOfB.aChar); |
|
CPPUNIT_ASSERT_EQUAL(2.5f, a2.childOfB.aFloat); |
|
CPPUNIT_ASSERT_EQUAL((short)-127, a2.childOfB.aShort); |
|
} |
|
void complexSave() { |
|
A2 a2; |
|
a2._anInteger = -1234; |
|
a2._aBool = true; |
|
a2._aDouble = 3.141; |
|
a2._aString = std::string("This is A inside of A2"); |
|
a2._anotherString = std::string("Another A-String"); |
|
a2._aLong = 1234567890ul; |
|
a2.aText = std::string("Text from A2"); |
|
a2.childOfB.aLong = 987654321l; |
|
a2.childOfB.aChar = 'Q'; |
|
a2.childOfB.aFloat = 2.5f; |
|
a2.childOfB.aShort = (short)-127; |
|
std::stringstream ss; |
|
a2.saveXml(ss); |
|
CPPUNIT_ASSERT_EQUAL |
|
(std::string("<A2>\n" |
|
"\t<anInteger>-1234</anInteger>\n" |
|
"\t<aBool>true</aBool>\n" |
|
"\t<aDouble>3.141</aDouble>\n" |
|
"\t<aString>This is A inside of A2</aString>\n" |
|
"\t<anotherString>Another A-String</anotherString>\n" |
|
"\t<aLong>1234567890</aLong>\n" |
|
"\t<aText>Text from A2</aText>\n" |
|
"\t<childOfB>\n" |
|
"\t\t<aLong>987654321</aLong>\n" |
|
"\t\t<aChar>Q</aChar>\n" |
|
"\t\t<float>2.5</float>\n" |
|
"\t\t<short>-127</short>\n" |
|
"\t</childOfB>\n" |
|
"</A2>"), ss.str()); |
|
} |
|
void moreComplexLoad() { |
|
std::stringstream ss |
|
("<C>\n" |
|
" <a2>\n" |
|
" <anInteger>-1234</anInteger>\n" |
|
" <aBool>true</aBool>\n" |
|
" <aDouble>3.141</aDouble>\n" |
|
" <aString>This is A inside of A2</aString>\n" |
|
" <anotherString>Another A-String</anotherString>\n" |
|
" <aLong>1234567890</aLong>\n" |
|
" <aText>Text from A2</aText>\n" |
|
" <childOfB>\n" |
|
" <aLong>987654321</aLong>\n" |
|
" <aChar>Q</aChar>\n" |
|
" <float>2.5</float>\n" |
|
" <short>-127</short>\n" |
|
" </childOfB>\n" |
|
" </a2>\n" |
|
" <b2>\n" |
|
" <aLong>212121</aLong>\n" |
|
" <aChar>W</aChar>\n" |
|
" <float>2.25</float>\n" |
|
" <short>124</short>\n" |
|
" </b2>\n" |
|
"</C>"); |
|
C c; |
|
CPPUNIT_ASSERT_EQUAL(std::string("<C>\n" |
|
"\t<a2>\n" |
|
"\t\t<anInteger/>\n" |
|
"\t\t<aBool/>\n" |
|
"\t\t<aDouble/>\n" |
|
"\t\t<aString/>\n" |
|
"\t\t<anotherString/>\n" |
|
"\t\t<aLong/>\n" |
|
"\t\t<aText/>\n" |
|
"\t\t<childOfB>\n" |
|
"\t\t\t<aLong/>\n" |
|
"\t\t\t<aChar/>\n" |
|
"\t\t\t<float/>\n" |
|
"\t\t\t<short/>\n" |
|
"\t\t</childOfB>\n" |
|
"\t</a2>\n" |
|
"\t<b2>\n" |
|
"\t\t<aLong/>\n" |
|
"\t\t<aChar/>\n" |
|
"\t\t<float/>\n" |
|
"\t\t<short/>\n" |
|
"\t</b2>\n" |
|
"</C>"), c.schema()); |
|
CPPUNIT_ASSERT_NO_THROW(c.loadXml(ss)); |
|
CPPUNIT_ASSERT_EQUAL(-1234, c.a2._anInteger); |
|
CPPUNIT_ASSERT_EQUAL(true, c.a2._aBool); |
|
CPPUNIT_ASSERT_EQUAL(3.141, c.a2._aDouble); |
|
CPPUNIT_ASSERT_EQUAL(std::string("This is A inside of A2"), |
|
c.a2._aString); |
|
CPPUNIT_ASSERT_EQUAL(std::string("Another A-String"), |
|
c.a2._anotherString); |
|
CPPUNIT_ASSERT_EQUAL(1234567890ul, c.a2._aLong); |
|
CPPUNIT_ASSERT_EQUAL(std::string("Text from A2"), c.a2.aText); |
|
CPPUNIT_ASSERT_EQUAL(987654321l, c.a2.childOfB.aLong); |
|
CPPUNIT_ASSERT_EQUAL('Q', c.a2.childOfB.aChar); |
|
CPPUNIT_ASSERT_EQUAL(2.5f, c.a2.childOfB.aFloat); |
|
CPPUNIT_ASSERT_EQUAL((short)-127, c.a2.childOfB.aShort); |
|
CPPUNIT_ASSERT_EQUAL(212121l, c.b2.aLong); |
|
CPPUNIT_ASSERT_EQUAL('W', c.b2.aChar); |
|
CPPUNIT_ASSERT_EQUAL(2.25f, c.b2.aFloat); |
|
CPPUNIT_ASSERT_EQUAL((short)124, c.b2.aShort); |
|
} |
|
void moreComplexSave() { |
|
C c; |
|
c.a2._anInteger = -1234; |
|
c.a2._aBool = true; |
|
c.a2._aDouble = 3.141; |
|
c.a2._aString = std::string("This is A inside of A2"); |
|
c.a2._anotherString = std::string("Another A-String"); |
|
c.a2._aLong = 1234567890ul; |
|
c.a2.aText = std::string("Text from A2"); |
|
c.a2.childOfB.aLong = 987654321l; |
|
c.a2.childOfB.aChar = 'Q'; |
|
c.a2.childOfB.aFloat = 2.5f; |
|
c.a2.childOfB.aShort = (short)-127; |
|
c.b2.aLong = 212121l; |
|
c.b2.aChar = 'W'; |
|
c.b2.aFloat = 2.25f; |
|
c.b2.aShort = (short)124; |
|
std::stringstream ss; |
|
c.saveXml(ss); |
|
CPPUNIT_ASSERT_EQUAL |
|
(std::string("<C>\n" |
|
"\t<a2>\n" |
|
"\t\t<anInteger>-1234</anInteger>\n" |
|
"\t\t<aBool>true</aBool>\n" |
|
"\t\t<aDouble>3.141</aDouble>\n" |
|
"\t\t<aString>This is A inside of A2</aString>\n" |
|
"\t\t<anotherString>Another A-String</anotherString>\n" |
|
"\t\t<aLong>1234567890</aLong>\n" |
|
"\t\t<aText>Text from A2</aText>\n" |
|
"\t\t<childOfB>\n" |
|
"\t\t\t<aLong>987654321</aLong>\n" |
|
"\t\t\t<aChar>Q</aChar>\n" |
|
"\t\t\t<float>2.5</float>\n" |
|
"\t\t\t<short>-127</short>\n" |
|
"\t\t</childOfB>\n" |
|
"\t</a2>\n" |
|
"\t<b2>\n" |
|
"\t\t<aLong>212121</aLong>\n" |
|
"\t\t<aChar>W</aChar>\n" |
|
"\t\t<float>2.25</float>\n" |
|
"\t\t<short>124</short>\n" |
|
"\t</b2>\n" |
|
"</C>"), ss.str()); |
|
} |
|
CPPUNIT_TEST_SUITE(SerializationTest); |
|
CPPUNIT_TEST(memberDeclaration); |
|
CPPUNIT_TEST(store); |
|
CPPUNIT_TEST(restore); |
|
CPPUNIT_TEST(complexLoad); |
|
CPPUNIT_TEST(complexSave); |
|
CPPUNIT_TEST(moreComplexLoad); |
|
CPPUNIT_TEST(moreComplexSave); |
|
CPPUNIT_TEST_SUITE_END(); |
|
}; |
|
CPPUNIT_TEST_SUITE_REGISTRATION(SerializationTest); |
|
|
|
int main(int argc, char** argv) try { |
|
std::ofstream ofs((*argv+std::string(".xml")).c_str()); |
|
CppUnit::TextUi::TestRunner runner; |
|
runner.setOutputter(new CppUnit::XmlOutputter(&runner.result(), ofs)); |
|
runner.addTest(CppUnit::TestFactoryRegistry::getRegistry().makeTest()); |
|
return runner.run() ? 0 : 1; |
|
} catch (std::exception& e) { |
|
std::cerr<<"***Exception: "<<e.what()<<std::endl; |
|
return 1; |
|
}
|
|
|