master
Marc Wäckerlin 16 years ago
parent 650e88e2c3
commit 9548b27052
  1. 21
      AUTHORS
  2. 22
      README
  3. 14
      configure.in
  4. 4
      doc/doxyfile.in
  5. 62
      doc/examples/address.cxx
  6. 14
      doc/examples/makefile.am
  7. 2
      doc/makefile.am
  8. 5
      src/makefile.am
  9. 67
      src/xml-cxx/xml.hxx
  10. 68
      src/xml.cxx

@ -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>

@ -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

@ -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

@ -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;
}

@ -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. &ltp&gt;&ltp&gt;&ltp&gt;&lt/p&gt;&lt/p&gt;&lt/p&gt;) (e.g. &ltp&gt;&ltp&gt;&ltp&gt;&lt/p&gt;&lt/p&gt;&lt/p&gt;)
- 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++.

@ -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 &lt;address&gt;
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);

Loading…
Cancel
Save