more docu
This commit is contained in:
21
AUTHORS
21
AUTHORS
@@ -0,0 +1,21 @@
|
|||||||
|
<address>
|
||||||
|
<name>
|
||||||
|
<first>Marc</first>
|
||||||
|
<middle>Roman</middle>
|
||||||
|
<last>Wäckerlin</last>
|
||||||
|
</name>
|
||||||
|
<location>
|
||||||
|
<line>SwissSign AG</line>
|
||||||
|
<line>Pfingstweidstrasse 60b</line>
|
||||||
|
<line>8005 Zürich</line>
|
||||||
|
</location>
|
||||||
|
<country>Schweiz</country>
|
||||||
|
<email>marc@waeckerlin.org</email>
|
||||||
|
<email>marc.waeckerlin@swisssign.com</email>
|
||||||
|
<url>http://dev.swisssign.com/trac/libxml-cxx</url>
|
||||||
|
<url>http://marc.wäckerlin.ch</url>
|
||||||
|
<url>http://marc.waeckerlin.org</url>
|
||||||
|
<url>http://dev.swisssign.com</url>
|
||||||
|
<url>http://swissign.com</url>
|
||||||
|
<url>http://swissign.ch</url>
|
||||||
|
</address>
|
||||||
|
22
README
22
README
@@ -0,0 +1,22 @@
|
|||||||
|
This is a C++ class for reading and writing XML structures.
|
||||||
|
|
||||||
|
All informaton can be found in the generated doxygen project documentation.
|
||||||
|
|
||||||
|
Rationale: The initial idea was to map C++ data structures to XML
|
||||||
|
files for configuration files that can easily be edited by hand. This
|
||||||
|
library does not need any kind of C++ code parser or special pre
|
||||||
|
compiler. You can specify a schema entirly in native C++. 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.
|
||||||
|
|
||||||
|
(More rationale: See also "related Pages" in the doxygen project documentation)
|
||||||
|
|
||||||
|
Structure of the files:
|
||||||
|
src: The source code of the library
|
||||||
|
doc/html: Doxygen documentation oft the library usage
|
||||||
|
doc/examples: Example code (included in doxygen documentation)
|
||||||
|
test: CppUnit test files - can also be taken as examples
|
||||||
|
|
||||||
|
Project URL: http://dev.swisssign.com
|
||||||
|
14
configure.in
14
configure.in
@@ -12,7 +12,7 @@ AM_INIT_AUTOMAKE(x_packagename, x_major.x_minor.x_least, [marc@waeckerlin.org])
|
|||||||
|
|
||||||
# files to create
|
# files to create
|
||||||
AC_CONFIG_FILES(makefile
|
AC_CONFIG_FILES(makefile
|
||||||
src/makefile test/makefile
|
src/makefile test/makefile doc/examples/makefile
|
||||||
doc/doxyfile doc/makefile)
|
doc/doxyfile doc/makefile)
|
||||||
|
|
||||||
# copy M4 to shell
|
# copy M4 to shell
|
||||||
@@ -71,18 +71,6 @@ AC_ARG_ENABLE(dot,
|
|||||||
test "$enableval" = "yes" && HAVE_DOT="YES" || HAVE_DOT="NO";
|
test "$enableval" = "yes" && HAVE_DOT="YES" || HAVE_DOT="NO";
|
||||||
AM_PATH_CPPUNIT([1.0.0], [have_cppunit="yes"], [have_cppunit="no"])
|
AM_PATH_CPPUNIT([1.0.0], [have_cppunit="yes"], [have_cppunit="no"])
|
||||||
|
|
||||||
# Special Options
|
|
||||||
AC_ARG_ENABLE(win,
|
|
||||||
[AS_HELP_STRING([--enable-win],
|
|
||||||
[on linux, also builds windows version using mingw])],
|
|
||||||
[build_win="$enableval"], [build_win="no"])
|
|
||||||
AM_CONDITIONAL(BUILD_WIN, test "$build_win" = "yes")
|
|
||||||
AC_ARG_ENABLE(32bit-linux,
|
|
||||||
[AS_HELP_STRING([--enable-32bit-linux],
|
|
||||||
[build for 32bit linux instead of plattform specific])],
|
|
||||||
[build_lin32="$enableval"], [build_lin32="no"])
|
|
||||||
AM_CONDITIONAL(BUILD_LIN32, test "$build_lin32" = "yes")
|
|
||||||
|
|
||||||
# export macros
|
# export macros
|
||||||
SRCDIR=${srcdir}
|
SRCDIR=${srcdir}
|
||||||
AC_SUBST(SRCDIR)
|
AC_SUBST(SRCDIR)
|
||||||
|
@@ -60,7 +60,7 @@ CREATE_SUBDIRS = NO
|
|||||||
# Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish,
|
# Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish,
|
||||||
# and Ukrainian.
|
# and Ukrainian.
|
||||||
|
|
||||||
OUTPUT_LANGUAGE = German
|
OUTPUT_LANGUAGE = English
|
||||||
|
|
||||||
# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
|
# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
|
||||||
# include brief member descriptions after the members that are listed in
|
# include brief member descriptions after the members that are listed in
|
||||||
@@ -578,7 +578,7 @@ EXCLUDE_SYMBOLS =
|
|||||||
# directories that contain example code fragments that are included (see
|
# directories that contain example code fragments that are included (see
|
||||||
# the \include command).
|
# the \include command).
|
||||||
|
|
||||||
EXAMPLE_PATH = .
|
EXAMPLE_PATH = @SRCDIR@/..
|
||||||
|
|
||||||
# If the value of the EXAMPLE_PATH tag contains directories, you can use the
|
# If the value of the EXAMPLE_PATH tag contains directories, you can use the
|
||||||
# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
|
# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
|
||||||
|
62
doc/examples/address.cxx
Normal file
62
doc/examples/address.cxx
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
/*! @file
|
||||||
|
|
||||||
|
@id $Id$
|
||||||
|
*/
|
||||||
|
// 1 2 3 4 5 6 7 8
|
||||||
|
// 45678901234567890123456789012345678901234567890123456789012345678901234567890
|
||||||
|
|
||||||
|
// g++ -I../src ../src/xml.cxx address.cxx
|
||||||
|
|
||||||
|
#include <xml-cxx/xml.hxx>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
int main(int, char**) try {
|
||||||
|
// Example template in factory instantiation
|
||||||
|
xml::Factory addrTpl(xml::Node("address")
|
||||||
|
<<(xml::Node("name").limits(1, 1)
|
||||||
|
<<xml::String("first").limits(1, 1)
|
||||||
|
<<xml::String("middle") // 0..n -> .limit(0, 0)
|
||||||
|
<<xml::String("last").limits(1, 1))
|
||||||
|
<<(xml::Node("location").max(1)
|
||||||
|
<<xml::String("line").min(1))
|
||||||
|
<<xml::String("email")
|
||||||
|
<<xml::String("url")
|
||||||
|
<<xml::String("country").max(1));
|
||||||
|
// Example XML file to read
|
||||||
|
std::stringstream ss("\
|
||||||
|
<address>\
|
||||||
|
<name>\
|
||||||
|
<first>Marc</first>\
|
||||||
|
<middle>Roman</middle>\
|
||||||
|
<last>Wäckerlin</last>\
|
||||||
|
</name>\
|
||||||
|
<location>\
|
||||||
|
<line>SwissSign AG</line>\
|
||||||
|
<line>Pfingstweidstrasse 60b</line>\
|
||||||
|
<line>8005 Zürich</line>\
|
||||||
|
</location>\
|
||||||
|
<country>Schweiz</country>\
|
||||||
|
<email>marc@waeckerlin.org</email>\
|
||||||
|
<email>marc.waeckerlin@swisssign.com</email>\
|
||||||
|
<url>http://dev.swisssign.com/trac/libxml-cxx</url>\
|
||||||
|
<url>http://marc.wäckerlin.ch</url>\
|
||||||
|
<url>http://marc.waeckerlin.org</url>\
|
||||||
|
<url>http://dev.swisssign.com</url>\
|
||||||
|
<url>http://swissign.com</url>\
|
||||||
|
<url>http://swissign.ch</url>\
|
||||||
|
</address>");
|
||||||
|
std::auto_ptr<xml::Node> author(addrTpl.read(ss));
|
||||||
|
// write to stdout
|
||||||
|
std::cout<<"Successfully read:"<<std::endl
|
||||||
|
<<"------------------------------"<<std::endl
|
||||||
|
<<*author<<std::endl
|
||||||
|
<<"------------------------------"<<std::endl;
|
||||||
|
// store in project's AUTHORS file
|
||||||
|
std::ofstream ofs("../AUTHORS");
|
||||||
|
ofs<<*author<<std::endl;
|
||||||
|
return 0;
|
||||||
|
} catch (std::exception& e) {
|
||||||
|
std::cerr<<"**** Error: "<<e.what()<<std::endl;
|
||||||
|
}
|
14
doc/examples/makefile.am
Normal file
14
doc/examples/makefile.am
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
## @id $Id$
|
||||||
|
|
||||||
|
## 1 2 3 4 5 6 7 8
|
||||||
|
## 45678901234567890123456789012345678901234567890123456789012345678901234567890
|
||||||
|
|
||||||
|
AM_CXXFLAGS += -I ${top_srcdir}/src
|
||||||
|
AM_LDFLAGS = -L${top_builddir}/src
|
||||||
|
|
||||||
|
noinst_PROGRAMS = address
|
||||||
|
|
||||||
|
address_SOURCES = address.cxx
|
||||||
|
address_LDADD = -lxml-cxx
|
||||||
|
|
||||||
|
MAINTAINERCLEANFILES = makefile.in
|
@@ -5,6 +5,8 @@
|
|||||||
## 1 2 3 4 5 6 7 8
|
## 1 2 3 4 5 6 7 8
|
||||||
## 45678901234567890123456789012345678901234567890123456789012345678901234567890
|
## 45678901234567890123456789012345678901234567890123456789012345678901234567890
|
||||||
|
|
||||||
|
SUBDIRS = examples
|
||||||
|
|
||||||
develdir = ${pkgdatadir}/doc
|
develdir = ${pkgdatadir}/doc
|
||||||
devel_DATA = html/index.html
|
devel_DATA = html/index.html
|
||||||
|
|
||||||
|
@@ -11,9 +11,4 @@ nobase_include_HEADERS = xml-cxx/xml.hxx
|
|||||||
|
|
||||||
libxml_cxx_la_SOURCES = xml.cxx
|
libxml_cxx_la_SOURCES = xml.cxx
|
||||||
|
|
||||||
if BUILD_WIN
|
|
||||||
else
|
|
||||||
endif
|
|
||||||
|
|
||||||
CLEANFILES =
|
|
||||||
MAINTAINERCLEANFILES = makefile.in
|
MAINTAINERCLEANFILES = makefile.in
|
||||||
|
@@ -15,17 +15,72 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
/*! @page limitations Known Limitations
|
/*! @mainpage
|
||||||
|
|
||||||
- Templates cannot specify number of sub-elements.
|
@section maintitle C++ XML Class Library
|
||||||
- XML-Comments are only ignored.
|
|
||||||
|
- 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.
|
||||||
- Mixed tags and text is not supported. Tags may either contain
|
- Mixed tags and text is not supported. Tags may either contain
|
||||||
other tags only (type xml::Node) or text only (type
|
other tags only (type xml::Node) or text only (type
|
||||||
xml::String).
|
xml::String). -> This is intended behaviour!
|
||||||
- Unlimited recursion is not possible
|
- Unlimited recursion is not possible
|
||||||
(e.g. <p><p><p></p></p></p>)
|
(e.g. <p><p><p></p></p></p>)
|
||||||
- No check yet for optional and mandatory attributes in xml::Factory
|
- Exceptions should be optional, best effort otherwise (option "strict")
|
||||||
- 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 doc/examples/address.cxx */
|
||||||
|
|
||||||
//! Represents classes for handling C++ access to XML files with strict schema.
|
//! 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++.
|
/*! The schema ist not presented through xsd, but it can be declared in C++.
|
||||||
|
68
src/xml.cxx
68
src/xml.cxx
@@ -244,7 +244,7 @@ namespace xml {
|
|||||||
//! Copy node, reset parent.
|
//! Copy node, reset parent.
|
||||||
/*! The parent is reset, the node does not belong to the same parent
|
/*! The parent is reset, the node does not belong to the same parent
|
||||||
as the source of the copy.
|
as the source of the copy.
|
||||||
@see xml::Node::clone for more information on the parenting. */
|
@see xml::Node::clone() for more information on the parenting. */
|
||||||
Node::Node(const Node& o) throw():
|
Node::Node(const Node& o) throw():
|
||||||
_attributes(o._attributes), _name(o.name()), _parent(0),
|
_attributes(o._attributes), _name(o.name()), _parent(0),
|
||||||
_min(o._min), _max(o._max) {
|
_min(o._min), _max(o._max) {
|
||||||
@@ -255,7 +255,7 @@ namespace xml {
|
|||||||
//! Assign new node, keep parent.
|
//! Assign new node, keep parent.
|
||||||
/*! The parent remains unchanged, the node does not belong to the
|
/*! The parent remains unchanged, the node does not belong to the
|
||||||
same parent as the source of the copy.
|
same parent as the source of the copy.
|
||||||
@see xml::Node::clone for more information on the parenting. */
|
@see xml::Node::clone() for more information on the parenting. */
|
||||||
Node& Node::operator=(const Node& o) throw() {
|
Node& Node::operator=(const Node& o) throw() {
|
||||||
_attributes=o._attributes;
|
_attributes=o._attributes;
|
||||||
_name = o.name();
|
_name = o.name();
|
||||||
@@ -270,7 +270,18 @@ namespace xml {
|
|||||||
}
|
}
|
||||||
//! Clones But clears the parent.
|
//! Clones But clears the parent.
|
||||||
/*! You get a new instance of the node, but detached from the
|
/*! You get a new instance of the node, but detached from the
|
||||||
parent. It is then ready to be inserted below a new parent. */
|
parent. It is then ready to be inserted below a new parent.
|
||||||
|
|
||||||
|
If you clone (or copy) a node, the parent is not copied. This is
|
||||||
|
because otherwise the new now would need to be appended to the
|
||||||
|
parent's child list. That's most probably not
|
||||||
|
intended. Therefore, in a normal copy construction the parent is
|
||||||
|
set to @c 0 (no parent). Then the new node can be appended to a
|
||||||
|
parent if needed. In a copy assignment, the parent remains
|
||||||
|
uncanged and the other data is copied.
|
||||||
|
|
||||||
|
The user of this library doesn't have to and is not able to care
|
||||||
|
about the parenting. */
|
||||||
std::auto_ptr<Node> Node::clone() const throw() {
|
std::auto_ptr<Node> Node::clone() const throw() {
|
||||||
std::auto_ptr<Node> res(new Node(*this));
|
std::auto_ptr<Node> res(new Node(*this));
|
||||||
res->_parent = 0;
|
res->_parent = 0;
|
||||||
@@ -544,13 +555,6 @@ namespace xml {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
//! Clone a node, but assign a new parent.
|
//! Clone a node, but assign a new parent.
|
||||||
/*! If you clone (or copy) a node, the parent is not copied. This is
|
|
||||||
because otherwise the new now would need to be appended to the
|
|
||||||
parent's child list. That's most probably not
|
|
||||||
intended. Therefore, in a normal copy operation (copy-construct
|
|
||||||
or copy-assign) the parent is set to @c 0 (no parent). Then the
|
|
||||||
new node can be appended to a parent if needed. In this method,
|
|
||||||
the new parent can be given in the same step. */
|
|
||||||
std::auto_ptr<Node> Node::clone(Node* p) const throw() {
|
std::auto_ptr<Node> Node::clone(Node* p) const throw() {
|
||||||
std::auto_ptr<Node> c(clone());
|
std::auto_ptr<Node> c(clone());
|
||||||
c->_parent = p;
|
c->_parent = p;
|
||||||
@@ -643,9 +647,11 @@ namespace xml {
|
|||||||
_text = ss.str();
|
_text = ss.str();
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
//! Returns the contents as number.
|
||||||
unsigned long UnsignedInteger::number() const throw() {
|
unsigned long UnsignedInteger::number() const throw() {
|
||||||
return number(*this);
|
return number(*this);
|
||||||
}
|
}
|
||||||
|
//! Returns the contents as number.
|
||||||
unsigned long UnsignedInteger::number(const Node& node) throw() {
|
unsigned long UnsignedInteger::number(const Node& node) throw() {
|
||||||
unsigned long i(0);
|
unsigned long i(0);
|
||||||
std::stringstream ss(node.text());
|
std::stringstream ss(node.text());
|
||||||
@@ -654,6 +660,48 @@ namespace xml {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
|
//! To instanciate a factory, a template must be given.
|
||||||
|
/*! The template is a node that specifies the schema of the data
|
||||||
|
that can be loaded from streams through a xml::Factory
|
||||||
|
instance.
|
||||||
|
|
||||||
|
The root element has automatically set the limits 1..1
|
||||||
|
(<code>xml::Node::limits(1, 1)</code>, see xml::Node::limits),
|
||||||
|
which means that the root element must exist exactly once. If
|
||||||
|
you pass another limit, your limit is overwritten.
|
||||||
|
|
||||||
|
E.g. to load an address, that contains a tag <address>
|
||||||
|
with at least a name and optional an address in it's body, you
|
||||||
|
may write:
|
||||||
|
|
||||||
|
@code
|
||||||
|
xml::Factory addrTpl(xml::Node("address")
|
||||||
|
(<<xml::Node("name").limit(1, 1)
|
||||||
|
(<<xml::String("first").limit(1, 1)
|
||||||
|
<<xml::String("middle") // 0..n -> .limit(0, 0)
|
||||||
|
<<xml::String("last").limit(1, 1))
|
||||||
|
<<(xml::Node("location").max(1)
|
||||||
|
<<xml::String("line").min(1))
|
||||||
|
<<xml::String("country").max(1)));
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
According to this example, a valid XML file could be:
|
||||||
|
|
||||||
|
@verbatim
|
||||||
|
<address>
|
||||||
|
<name>
|
||||||
|
<first>Marc</first>
|
||||||
|
<middle>Roman</middle>
|
||||||
|
<last>Wäckerlin</last>
|
||||||
|
</name>
|
||||||
|
<location>
|
||||||
|
<line>SwissSign AG</line>
|
||||||
|
<line>Pfingstweidstrasse 60b</line>
|
||||||
|
<line>8005 Zürich</line>
|
||||||
|
</location>
|
||||||
|
<country>Schweiz</country>
|
||||||
|
</address>
|
||||||
|
@endverbatim */
|
||||||
Factory::Factory(const Node& t) throw():
|
Factory::Factory(const Node& t) throw():
|
||||||
_template(xml::Node("<xml::start>")<<t), _line(0) {
|
_template(xml::Node("<xml::start>")<<t), _line(0) {
|
||||||
_template[0].min(1);
|
_template[0].min(1);
|
||||||
|
Reference in New Issue
Block a user