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.

919 lines
36 KiB

16 years ago
/*! @file
@id $Id$
*/
// 1 2 3 4 5 6 7 8
// 45678901234567890123456789012345678901234567890123456789012345678901234567890
16 years ago
#ifndef LIB_XML_CXX_HXX
#define LIB_XML_CXX_HXX
16 years ago
#include <istream>
#include <sstream>
16 years ago
#include <string>
#include <vector>
#include <list>
#include <set>
16 years ago
#include <map>
#include <memory>
#include <typeinfo>
#include <stdexcept>
#include <boost/any.hpp>
16 years ago
#include <cassert>
#include <iostream>
#include <iomanip>
class MethodTrace {
public:
MethodTrace(const void* addr, const std::string& name) throw():
_addr(addr), _name(name) {
std::clog<<std::hex<<std::setw(15)<<_addr<<": "
<<std::dec<<std::setw(2+_level)
<<std::setfill(' ')<<"\\ "<<_name<<std::endl;
++_level;
}
~MethodTrace() throw() {
--_level;
std::clog<<std::hex<<std::setw(15)<<_addr<<": "<<std::dec
<<std::setw(2+_level)<<std::setfill(' ')
<<"/ "<<_name<<std::endl;
}
private:
const void* _addr;
const std::string _name;
static unsigned int _level;
};
#define TRACE MethodTrace XXX_METHOD(this, __PRETTY_FUNCTION__)
#define LOG(X) std::clog<<__PRETTY_FUNCTION__<<"\t**** "<<X<<std::endl
#define ASSERT(X, Y) { \
if (!(Y)) { \
LOG(X); \
} \
assert(Y); \
}
16 years ago
/*! @mainpage
16 years ago
16 years ago
@section maintitle C++ XML Class Library
- Specify your XML schema in C++ using common C++ syntax,
such as shift, dereference, etc.
- Verify the schema of XML files while they are read from a stream.
From the README file:
@include README
@page Known Limitations
- XML-Comments are only ignored, not read, not stored.
16 years ago
- Mixed tags and text is not supported. Tags may either contain
other tags only (type xml::Node) or text only (type
16 years ago
xml::String). -> This is intended behaviour!
16 years ago
- Unlimited recursion is not possible
(e.g. &ltp&gt;&ltp&gt;&ltp&gt;&lt/p&gt;&lt/p&gt;&lt/p&gt;)
16 years ago
- Exceptions should be optional, best effort otherwise (option "strict")
@page rationale Rationale - Limitations of other libraries
The initial idea was to map C++ data structures to XML files
(e.g. for configuration files) that can easily be edited by
hand. This library does not need any kind of C++ code parser or
proprietary pre compiler. You can specify a schema entirly in
native C++. Access to the XML structures is through typical C++
operators which rresults in a simple and intuitive syntax. The
schema is verified when XML is read and exceptions are thrown when
the XML to be pares is invalid. Exceptions specify exactly the
location and reason of the problem, so that the editor of the XML
file can easily find and correct the problem. Due to the
verification, it is not necessary to check every access: Only
optional attributes and tags must be tested for their existence,
before they can be accessed.
There are a lot of different approaches for using XML in C++, all
of them have their specific limitations. This library should be
better.
The design is based on my experiance with gsoap
(http://gsoap.sf.net), boost serialization (http://boost.org) and
Qt XML (http://qtsoftware.com).
@section Qt XML, a typical DOM approach
One is the XML part of the Qt library. These classes can read XML
into a DOM tree, but then the user has to check for every detail.
This looks like:
@code
QDomDocument doc;
if (!doc.setContent(_http.readAll())); // error
QDomNodeList releases(doc.elementsByTagName("release"));
for(int i(0); i<releases.size(); ++i)
if (releases.at(i).attributes().contains("type") &&
releases.at(i).attributes().namedItem("type").nodeValue()==_name) {
_software = releases.at(i).firstChildElement("software");
_firmware = releases.at(i).firstChildElement("firmware");
if (_software.isNull() || _firmware.isNull() ||
releases.at(i).firstChildElement("notes").isNull()); // error
...
@endcode
@example address.cxx */
16 years ago
/*! @defgroup xmlConst XML Constant Declarations
There are macros to help you with declaring constants. Chose a C++
header fiel, where you want to declare constant names for your xml
nodes.
Then for every leaf xml::Node name you will use, call
XML_NODE(name) and for every xml::String call XML_STRING(name).
For every node with children call XML_PARENT(name, child1, child2 ...).
@note Node names must be unique. You can not even use the same
name for a XML_NODE and a XML_STRING declaration.
This code is from the examples:
@include node_macros.cxx
@example node_macros.cxx
The example code is equivalent to:
@code
int main(int, char**) {
xml::Factory test(xml::Node("base")
<<(xml::Node("child")
<<xml::String("element"));
std::stringstream ss("<base>\n"
" <child>\n"
" <element>Hello</element>\n"
" </child>\n"
"</base>");
std::auto_ptr<xml::Node> file(test.read(ss));
std::cout<<"The element is: "
<<(*file)["child"]["element"]
<<std::endl;
return 0;
}
@endcode
The advantage is the fact that you have no more quoted strings to
cope with. So your potential runtime errors through typos become
compile time errors, which is more robust and easier to find. If
you want to write less code, you are free to use <code>using
namespace xml::name</code> in your C++ implementation files, since
names are more often used than nodes. (@em Never use @c using in a
C++ header file, you would pollute the scope of all the
includers.) */
//@{
//! Define a string for a node name
/*! It is called inside XML_NODE and XML_STRING, so if you work with
these two, you don't have to care about XML_NAME. But you can use
XML_NAME alone if you don't want the other two macros.
@see XML_NODE
@see XML_STRING */
#define XML_NAME(NAME) \
namespace xml {\
namespace name {\
static const std::string NAME(#NAME); \
}\
}
//! Define a constant for a xml::Node and for a string containing its name
/*! Put this macro in the global part of your header files. After
declaration of e.g. <code>XML_NODE(tagname)</code> you can use
<code>xml::node::tagname</code> as constant xml::Node and
<code>xml::name::tagname</code> as a constant std::string with
contents @c "tagname" in your code.
@see XML_STRING same for xml::String
@see XML_NAME called by XML_NODE */
#define XML_NODE(NAME) \
XML_NAME(NAME);\
namespace xml {\
namespace node {\
static const xml::Node NAME(#NAME);\
}\
}
//! Define a constant for a xml::String and for a string containing its name
/*! Put this macro in the global part of your header files. After
declaration of e.g. <code>XML_STRING(tagname)</code> you can use
<code>xml::string::tagname</code> as constant xml::String and
<code>xml::name::tagname</code> as a constant std::string with
contents @c "tagname" in your code.
@see XML_NODE same for xml::Node
@see XML_NAME called by XML_STRING */
#define XML_STRING(NAME) \
XML_NAME(NAME);\
namespace xml {\
namespace string {\
static const xml::String NAME(#NAME);\
}\
}
//! @todo Define a constant for a xml::Node and for a string containing its name
/*! Put this macro in the global part of your header files. After
declaration of e.g. <code>XML_NODE(tagname)</code> you can use
<code>xml::node::tagname</code> as constant xml::Node and
<code>xml::name::tagname</code> as a constant std::string with
contents @c "tagname" in your code.
@see XML_STRING same for xml::String
@see XML_NAME called by XML_NODE */
#define XML_PARENT(NAME, ...) \
XML_NAME(NAME);\
namespace xml {\
namespace node {\
static const xml::Node NAME(#NAME);\
}\
}
//@}
//! Represents classes for handling C++ access to XML files with strict schema.
/*! The schema ist not presented through xsd, but it can be declared in C++.
A xml::Factory represents a factory that owns a template and can
instanciate XML trees that are valid for the given template from
streams. If anything is not valid, an exception is thrown. The @c
what() method of the exception gives additional information about
the problem.
In the following example, we want to represent XML data that are
contained in a &lt;persons&gt; tag, and may contain a list of @c
person. Each @c person has a mandatory attribute @c id and
optional @c member-of. @c person has a @c name and may contain a
list of @c friends, where each @c friend has an attribute @c
id. (The @c id attribute of course should reference to the @c id
of another @c name, but this relationship cannot be declared.)
All tags are by default specified as 0..n (optional and any number
there of).
16 years ago
@code
#include <xml-cxx/xml.hxx>
#include <iostream>
[...]
xml::Factory test(xml::Node("persons") // root node
<<(xml::Node("person") // child of persons
.attr("id", xml::mandatory)
.attr("member-of", xml::optional))
<<xml::String("name") // the person's name
<<(xml::Node("friends") // friends of person
<<(xml::Node("friend") // a friend
.attr("id", xml::mandatory)))));
16 years ago
[...]
try {
std::auto_ptr<xml::Node> persons(test.read(std::ifstream("file.xml)));
// Here we can be sure, that our structure is valid,
// but we must check optional elements before access, otherwise
// we get an exception.
16 years ago
[...]
for (xml::Node::size_type i(0); i<persons.children(); ++i) {
std::cout<<"Person: "<<*persons[i]["name"]; // exception if no "name"
if (persons[i]("friends")) // check if "friends" is set
std::cout<<" has "<<persons[i]["friends"].children()<<" friends"
else
std::cout<<" has no friend list";
std::cout<<std::endl;
}
16 years ago
[...]
} catch (const std::exception& x) {
std::cerr<<"**** Error in file \"file.xml\":"<<std::endl
<<x.what()<<std::endl;
}
16 years ago
@endcode */
namespace xml {
16 years ago
//============================================================================
16 years ago
//! Type of an xml node.
/*! Only start nodes and empty nodes may have attributes. */
enum NodeType {
START, //!< start node, such as <code>&lt;node&gt;</code>
END, //!< end node, such as <code>&lt;/node&gt;</code>
EMPTY, //!< empty node, such as <code>&lt;node/&gt;</code>
SPECIAL //!< special node, such as
//! a comment <code>&lt;!-- ... --&gt;</code>,
//! a xml start indication <code>&lt;?xml?&gt;</code>
//! or a document type declaration <code>&lt;!DOCTYPE ...&gt;</code>
};
//! Declares an attribute to be mandatory.
const bool mandatory(true);
//! Declares an attribute to be optional.
const bool optional(false);
16 years ago
//================================================================= EXCEPTIONS
struct Tag;
class Attributes;
16 years ago
class Node;
class Factory;
//----------------------------------------------------------------------------
class exception: public std::exception {
public:
exception(std::string reason) throw();
16 years ago
exception(std::string reason, const Node& t) throw();
~exception() throw();
16 years ago
void line(unsigned long line) throw();
16 years ago
const char* what() const throw();
private:
std::string _what;
Node* _node;
};
//----------------------------------------------------------------------------
class empty_attribute_list: public exception {
public:
empty_attribute_list(const std::string& name) throw():
exception("list of attribute is empty access to first element failed"
"\nattribute: "+name) {
}
};
//----------------------------------------------------------------------------
class factory_not_valid: public exception {
public:
factory_not_valid() throw():
exception("a factory must be given a template node") {
}
};
//----------------------------------------------------------------------------
16 years ago
class no_parent: public exception {
public:
no_parent(const Node& t) throw(): exception("node has no parent", t) {}
};
//----------------------------------------------------------------------------
class tag_expected: public exception {
public:
tag_expected(const Node& t, const std::string& txt) throw():
exception("tag ('<') expected, text not allowed\ntext: "+txt, t) {}
};
//----------------------------------------------------------------------------
class type_mismatch: public exception {
public:
16 years ago
type_mismatch(const Node& t, const std::string& txt,
const std::string& comment) throw():
exception("wrong type, text contains mismatching character\n"+comment
+"\ntext: "+txt, t) {}
};
//----------------------------------------------------------------------------
16 years ago
class access_error: public exception {
public:
access_error(const Node& t, const std::string& name) throw();
~access_error() throw() {}
const char* what() const throw();
private:
std::string _name;
};
//----------------------------------------------------------------------------
class out_of_range: public exception {
public:
out_of_range(const Node& t, size_t pos) throw();
~out_of_range() throw() {}
const char* what() const throw();
private:
size_t _pos;
};
//----------------------------------------------------------------------------
class cannot_have_children: public exception {
public:
cannot_have_children(const Node& parent, const Node& child) throw();
~cannot_have_children() throw();
const char* what() const throw();
private:
Node* _child;
};
//----------------------------------------------------------------------------
class attribute_not_available: public exception {
public:
attribute_not_available(const Node& t, const std::string& attr) throw():
exception("attribute \""+attr+"\" not set", t) {
}
};
//----------------------------------------------------------------------------
16 years ago
class stream_error: public exception {
public:
stream_error(const std::string& reason, const Node& t,
std::istream& is, const Tag& tag, char c=0) throw();
stream_error(const std::string& reason, const Node& t,
std::istream& is) throw();
~stream_error() throw();
16 years ago
const char* what() const throw();
private:
std::istream::streampos _pos;
Tag* _tag;
16 years ago
char _char;
};
//----------------------------------------------------------------------------
class wrong_end_tag: public stream_error {
public:
wrong_end_tag(const Node& t, std::istream& is, const Tag& tag, char c=0)
16 years ago
throw():
stream_error("mismatching end tag", t, is, tag, c) {
}
};
class missing_end_tag: public stream_error {
public:
missing_end_tag(const Node& t, std::istream& is, const Tag& tag, char c=0)
16 years ago
throw():
stream_error("missing end tag, end of file reached", t, is, tag, c) {
}
};
class wrong_start_tag: public stream_error {
public:
wrong_start_tag(const Node& t, std::istream& is, const Tag& tag, char c=0)
16 years ago
throw():
stream_error("start tag does not match expected tag", t, is, tag, c) {
}
};
class second_slash_in_tag: public stream_error {
public:
second_slash_in_tag(const Node& t, std::istream& is, const Tag& tag,
char c=0)
16 years ago
throw():
stream_error("a tag may have no more than one slash", t, is, tag, c) {
}
};
class character_after_slash: public stream_error {
public:
character_after_slash(const Node& t, std::istream& is, const Tag& tag,
char c=0)
16 years ago
throw():
stream_error("unexpected character after empty-slash",
t, is, tag, c) {
}
};
class attributes_in_end_tag: public stream_error {
public:
attributes_in_end_tag(const Node& t, std::istream& is, const Tag& tag)
16 years ago
throw():
stream_error("attributes are not allowed in end tags",t, is, tag) {
}
};
class attribute_value_not_quoted: public stream_error {
public:
attribute_value_not_quoted(const Node& t, std::istream& is,
const Tag& tag,
16 years ago
char c, std::string attr) throw():
stream_error("attribute values must be quoted (\")\nattribute: "+attr,
t, is, tag, c) {
}
};
class duplicate_attribute: public stream_error {
public:
duplicate_attribute(const Node& t, std::istream& is, const Tag& tag,
16 years ago
std::string attr) throw():
stream_error("attribute duplicated\nattribute: "+attr,
t, is, tag, 0) {
}
};
class illegal_attribute: public stream_error {
public:
illegal_attribute(const Node& t, std::istream& is, const Tag& tag,
std::string attr) throw():
stream_error("illegal attribute found\nattribute: "+attr,
t, is, tag, 0) {
}
};
class mandatory_attribute_missing: public stream_error {
public:
mandatory_attribute_missing(const Node& t, std::istream& is,
const Tag& tag, std::string attr) throw():
stream_error("mandatory attribute missing\nattribute: "+attr,
t, is, tag, 0) {
}
};
class wrong_node_number: public stream_error {
public:
wrong_node_number(const Node& t, std::istream& is,
const std::string& name,
unsigned long num,
unsigned long min, unsigned long max) throw():
stream_error("wrong number of child nodes\nname of child: "+name
+"\nnumber of nodes: "+conv(num)
+"\nminimuml number: "+conv(min)
+"\nmaximum number: "+conv(max), t, is) {
}
private:
static std::string conv(unsigned long i) throw() {
std::stringstream ss;
ss<<i;
return ss.str();
}
};
16 years ago
//============================================================================
//----------------------------------------------------------------------------
//! Map for attribute values.
/*! Attributes can be set using method xml::Node::attr(). Check for
an attribute with xml::Node::hasAttr(). Attributes must be
unique, which means that every attribute must be set at maximum
once. This is corect: <code>&lt;node
attribute="value"&gt;</code>, this is not allowed:
<code>&lt;node attribute="value" attribute="value"&gt;</code> */
class Attributes: public std::map<std::string, std::string> {
public:
//! Attributes may contain a list of space separated values.
typedef std::vector<std::string> List;
//! Attribute values ar mainly a std::pair.
/*! In addition to a normal std::pair, attributes offer an
assignment operator to set the value, and can be constructed
16 years ago
as empty attribute, given only a key.
@note Simply use xml::Attr instead of xml::Attributes::Value. */
class Value: public value_type {
public:
16 years ago
Value(const value_type& o) throw();
Value(const std::string& name) throw();
Value(const std::string& name, const std::string& namevalue) throw();
16 years ago
Value& operator=(const std::string& value) throw();
const std::string& name() const throw();
const std::string& value() const throw();
std::string& value() throw();
operator bool() const throw();
operator unsigned long() const throw();
operator List() const throw();
List toList(const std::string& separators=" \t\n\r") const throw();
std::string front(const std::string& separators=" \t\n\r") const
throw(empty_attribute_list);
private:
Value(); // not implemented, key must always be given
};
Attributes() throw();
Attributes(const std::string& empty) throw();
16 years ago
Attributes(const std::string& key, const std::string& value) throw();
Attributes& operator<<(const Value& v) throw();
Attributes& operator<<(const std::string& key) throw();
};
16 years ago
//! Simplification: Use xml::Attr instead of xml::Attributes::Value.
typedef Attributes::Value Attr;
//----------------------------------------------------------------------------
16 years ago
//! @internal structure for parsing tags
struct Tag {
std::string name;
NodeType type;
std::string text;
Attributes attributes;
std::string special;
bool found;
};
16 years ago
//----------------------------------------------------------------------------
16 years ago
//! An xml Node.
/*! XML Nodes may contain either text or other nodes, but not both
at the same time. This node can hold other nodes. For a Node for
text contents, see xml::String. */
16 years ago
class Node {
private:
typedef std::vector<Node*> Contents;
public:
typedef Contents::size_type size_type;
typedef std::vector<Node*> List;
Node(std::string name, size_type min=0, size_type max=0) throw();
16 years ago
Node(const Node& o) throw();
virtual ~Node() throw();
virtual Node& operator=(const Node& o) throw();
16 years ago
virtual std::auto_ptr<Node> clone() const throw();
virtual std::ostream& out(std::ostream& o, unsigned int level=0) const
throw();
virtual std::string text() const throw();
virtual Node& text(const std::string& txt) throw(tag_expected,
type_mismatch);
16 years ago
virtual Node& append(const Node& o) throw(cannot_have_children);
virtual Node& set(const Attributes& o) throw();
Node& clear() throw ();
std::string name() const throw();
Node& name(const std::string& n) throw();
Node& min(size_type m) throw();
size_type min() const throw();
Node& max(size_type m) throw();
size_type max() const throw();
16 years ago
bool isChild() const throw();
Node& parent() const throw(no_parent);
bool hasAttr(const std::string& name) const throw();
Node& attr(const std::string& name, bool mandatory) throw();
Node& attr(const std::string& name, const std::string& deflt) throw();
16 years ago
std::string attr(const std::string& name) const throw();
std::string& attr(const std::string& name) throw();
const Attributes::Value attribute(const std::string& name)
const throw(attribute_not_available);
const Attributes& attributes() const throw();
Attributes& attributes() throw();
Node& limits(size_type min=0, size_type max=0) throw();
List list(const std::string& name) const throw();
16 years ago
bool operator()(const std::string& child) const throw();
Node& operator<<(const Node& o) throw(cannot_have_children);
Node& operator<<(const Attributes& o) throw();
size_type children() const throw();
const Node& operator[](size_type child) const throw(out_of_range);
Node& operator[](size_type child) throw(out_of_range);
16 years ago
const Node& operator[](const std::string& child) const
throw(access_error);
Node& operator[](const std::string& child) throw(access_error);
std::string operator*() const throw();
Node& operator=(const std::string& contents) throw(tag_expected,
type_mismatch);
16 years ago
friend std::ostream& operator<<(std::ostream& o, const Node& t) throw();
protected:
Attributes _attributes;
private:
Node* find(const std::string& child) const throw();
virtual std::auto_ptr<Node> clone(Node* p) const throw();
Node(); // not implemented
Contents _contents;
std::string _name;
Node* _parent;
size_type _min;
size_type _max;
16 years ago
};
//----------------------------------------------------------------------------
class String: public Node {
public:
String(std::string name,
Node::size_type min=0, Node::size_type max=0) throw();
String(std::string name, const std::string& text,
Node::size_type min=0, Node::size_type max=0) throw();
16 years ago
virtual ~String() throw() {}
virtual std::auto_ptr<Node> clone() const throw();
16 years ago
virtual std::string text() const throw();
virtual String& text(const std::string& txt) throw(tag_expected,
type_mismatch);
virtual std::ostream& out(std::ostream& o, unsigned int level=0) const
throw();
virtual String& append(const Node& o) throw(cannot_have_children);
Node& operator=(const std::string& contents) throw();
operator std::string() const throw();
operator bool() const throw();
operator char() const throw();
operator signed char() const throw();
operator unsigned char() const throw();
operator signed short() const throw();
operator unsigned short() const throw();
operator signed int() const throw();
operator unsigned int() const throw();
operator signed long() const throw();
operator unsigned long() const throw();
operator float() const throw();
operator double() const throw();
16 years ago
protected:
std::string _text;
};
//----------------------------------------------------------------------------
16 years ago
class UnsignedInteger: public String {
public:
UnsignedInteger(std::string name, unsigned long i=0,
size_type min=0, size_type max=0) throw();
virtual std::auto_ptr<Node> clone() const throw();
16 years ago
virtual ~UnsignedInteger() throw() {}
virtual UnsignedInteger& text(const std::string& txt)
throw(tag_expected, type_mismatch);
unsigned long number() const throw();
static unsigned long number(const Node& node) throw();
16 years ago
};
//----------------------------------------------------------------------------
class Factory {
public:
Factory(const Node& t) throw();
Factory() throw();
Factory& operator=(const Node& t) throw();
Factory& append(const Node& node) throw();
const Node& operator*() const throw(factory_not_valid);
const Node*const operator->() const throw(factory_not_valid);
operator bool() const throw();
friend std::ostream& operator<<(std::ostream& os,
const Factory& factory) throw();
static std::ostream& print(std::ostream& os, const Node& node,
unsigned int level=0) throw();
16 years ago
std::auto_ptr<Node> read(std::istream& is)
throw(wrong_end_tag, wrong_start_tag, tag_expected, type_mismatch,
16 years ago
second_slash_in_tag, character_after_slash,
missing_end_tag, attribute_value_not_quoted, access_error,
duplicate_attribute, attributes_in_end_tag,
illegal_attribute, mandatory_attribute_missing,
wrong_node_number);
void reset() throw();
16 years ago
private:
friend class stream_error;
friend class Serialize;
Node& operator*() throw(factory_not_valid);
Node*const operator->() throw(factory_not_valid);
16 years ago
bool ws(char c) throw();
16 years ago
std::auto_ptr<Node> read(std::istream& is, const Node& position)
throw(wrong_end_tag, wrong_start_tag, tag_expected, type_mismatch,
16 years ago
second_slash_in_tag, character_after_slash,
missing_end_tag,
attribute_value_not_quoted, access_error, duplicate_attribute,
attributes_in_end_tag,
illegal_attribute, mandatory_attribute_missing,
wrong_node_number);
std::auto_ptr<Node> checkChildren(const xml::Node& tpl,
std::auto_ptr<Node> node,
std::istream& is) const
throw(wrong_node_number);
16 years ago
Tag tag(std::istream& is, const Node& position)
throw(second_slash_in_tag, wrong_start_tag, character_after_slash,
missing_end_tag, attributes_in_end_tag, tag_expected,
attribute_value_not_quoted, access_error, duplicate_attribute,
illegal_attribute, mandatory_attribute_missing);
Node _template;
16 years ago
unsigned long _line;
long _open;
16 years ago
};
/*! @defgroup serialization Class Serialization
@section serIntro Introduction
Boost library (http://boost.org) offers a serialization framework,
which is able to serialize even complex class structures, you only
need to overwrite one or two serialization macros. The
disadvantages are that a lot of macros are needed and it becomes
quite complex as soon as you need inheritance. Also the generated
XML is not very enhanced, especially for Lists and
optional. Editing the boost serialization code by hand is a pain.
Classes could also be serialized using gSOAP (http://gsoap.sf.net)
which is designed for the SOA-Protocol. This serialization is much
more flexible, but it requires a pseudo C++ declaration and a C++
parser/generator. Also it has very bad memory management, since it
is plain C internally.
Our requirements are:
- No precompiler, plain C++.
- Automatic memory management.
- Nice looking XML code that is easy to edit manually.
- Good error messages (exception) in case of bad XML files.
- As few ugly overflow as possible.
@section serActual Actual Status
The following member types are supported
- All built-in C++ types are supported
- std::string is supported
- Contained classes are supported
The following will be supported soon (ideas):
- lists, maps
- inheritance
- choices (one of)
- optional members (pointer)
Pointers cannot be stored.
There are many ways of implemenation. best practice is to
inherit xml::Serialize and to overwrite
xml::Serialize::initXmlMembers:
@code
class A: public xml::Serialize {
protected:
// all persitent members must be registered
virtual void initXmlMembers() {
className("A"); // giving a class name is very important
persist(_anInteger, "anInteger");
persist(_aBool, "aBool");
persist(_aDouble, "aDouble");
persist(_aString, "aString");
persist(_anotherString, "anotherString");
persist(_aLong, "aLong");
}
private:
int _anInteger;
bool _aBool;
double _aDouble;
std::string _aString;
std::string _anotherString;
unsigned long _aLong;
};
class B: public xml::Serialize {
protected:
virtual void initXmlMembers() {
className("B");
persist(_a); // name must not be given, it's already known
persist(_anInteger, "anInteger");
}
private:
A _a; // contains an A
int _anInteger;
};
int main(int, char**) {
A a;
B b;
// ... do something with a and b, then write it to stdout:
a.saveXml(std::out)<<std::endl;
b.saveXml(std::out)<<std::endl;
return 0;
}
@endcode
@example serialization.cxx
@example contain_serialization.cxx */
//@{
class Serialize {
public:
typedef bool(*FromNodeFunc)(boost::any, const xml::Node&);
typedef bool(*ToNodeFunc)(const boost::any, xml::Node&);
//! You must call Serialize::className() if you use this constructor!
Serialize() throw();
Serialize(const std::string& className) throw();
Serialize(const Serialize& other) throw();
virtual ~Serialize();
Serialize& operator=(const Serialize& other) throw();
Serialize& className(const std::string& name) throw();
Serialize& persist(Serialize& member,
const std::string& name) throw();
template<typename TYPE, class ALLOC>
Serialize& persist(std::list<TYPE, ALLOC>& member,
const std::string& name) throw() {
return persist(member, name, name);
}
template<typename TYPE, class ALLOC>
Serialize& persist(std::list<TYPE, ALLOC>& member,
const std::string& list,
const std::string& item) throw() {
_xmlNames[list] = &member;
Serialize ser(list);
TYPE dummy;
ser.persist(dummy, item);
*_xmlFactory<<(xml::Node(list).limits(1,1)
<<(*ser._xmlFactory)[0].limits(0, 0));
return *this;
}
Serialize& persist(bool& member,
const std::string& name) throw();
Serialize& persist(char& member,
const std::string& name) throw();
Serialize& persist(unsigned char& member,
const std::string& name) throw();
Serialize& persist(signed char& member,
const std::string& name) throw();
Serialize& persist(unsigned short& member,
const std::string& name) throw();
Serialize& persist(signed short& member,
const std::string& name) throw();
Serialize& persist(unsigned int& member,
const std::string& name) throw();
Serialize& persist(signed int& member,
const std::string& name) throw();
Serialize& persist(unsigned long& member,
const std::string& name) throw();
Serialize& persist(signed long& member,
const std::string& name) throw();
Serialize& persist(float& member,
const std::string& name) throw();
Serialize& persist(double& member,
const std::string& name) throw();
Serialize& persist(std::string& member,
const std::string& name) throw();
std::ostream& saveXml(std::ostream& os,
const std::string& name = std::string())
const throw();
std::istream& loadXml(std::istream& is,
const std::string& name = std::string());
std::string schema() const throw();
static void registerFromNode(FromNodeFunc fromNodeFunc);
static void registerToNode(ToNodeFunc toNodeFunc);
protected:
virtual void initXmlMembers();
private:
void clear() throw();
void copy(const Serialize& o) throw();
template<typename TYPE>
Serialize& persistSimpleType(TYPE& member,
const std::string& name) throw() {
_xmlNames[name] = &member;
xml::Node schema(*_xmlFactory);
schema<<xml::String(name).limits(1,1);
_xmlFactory = schema;
return *this;
}
void fromNode(boost::any member, const xml::Node& node);
void toNode(const boost::any member, xml::Node& node) const;
/*
template<typename TYPE, class ALLOC>
void fromNode(std::list<TYPE, ALLOC>* member, xml::Node& node) {
member->clear();
for (xml::Node::size_type i(0); i<node.children(); ++i)
...
}*/
std::map<std::string, boost::any> _xmlNames;
xml::Factory _xmlFactory;
static std::set<FromNodeFunc> _fromNode;
static std::set<ToNodeFunc> _toNode;
};
//@}
16 years ago
}
16 years ago
#endif