Contents

Introduction

The XML library

libzeep is a library that consists of two main parts. The first and arguably most useful for many is the XML processing part. It consists of a SAX parser, a DOM implementation, an XML writing module and an XPath implementation. The parser can act as a validating parser using the Document Type Definition to check the content. Schema support is not yet implemented but will be in a future release.

The second part of libzeep (and the part it all started with, hence the name) is a skeleton to build SOAP server implementations. It uses the XML part as well as various Boost libraries to achieve its usefulness. It allows the creation of a SOAP server by implementing only the serving routines in C++ and calling a routine for each to export of them. Code is then automatically generated to do all the SOAP message parsing and WSDL's generation occurs on-the-fly at runtime.

libzeep comes with headers and libraries. The headers all have zeep as namespace. All code heavily depends on the boost libraries.

Examples

Using libzeep is fairly straightforward. E.g., if we take the following XML file:

<?xml version="1.0" encoding="UTF-8"?>
<employees>
  <employee>
    <name id="1">Joe Doe</name>
    <address>Main street</address>
  </employee>
  <employee>
    <name id="2">Jack Daniels</name>
    <address>Backstreet</address>
  </employee>
</employees>

Reading this file is simple:

#include <zeep/xml/document.hpp>

using namespace zeep;
using namespace std;

ifstream data("/tmp/myfile.xml");
xml::document doc;
data >> doc;

The result of this code is a DOM tree with doc as root. An xml::document is derived from xml::element and can only have one xml::element child. Since it is a DOM tree, and since it is implemented in the classic link-list approach, you can use the next(), prev(), parent() and child() methods to navigate the tree.

But to stick to STL coding styles, walking a tree can also be done using the children() method which returns a std::list of pointers to its children. This method is templated since an element can have non-element child nodes like comments and processing instruction but most often you're only interested in the element child nodes.

Using the children method, iterating over all employees can be done like this:

// iterate over employees
xml::element_set employees = doc.root()->children<xml::element>();
for (xml::element_set::iterator employee = employees.begin(); employee != employees.end(); ++employee)
{
	// employee points to an element and we want its first child
	xml::element* name = (*employee)->child();
	// it really should be a name, right?
	assert(name->qname() == "name");
    cout << "Name: " << name->content() << endl;
}

Of course, using Boost's foreach this looks a lot nicer:

// iterate over employees, boost foreach style
#include <boost/foreach.hpp>
#define foreach BOOST_FOREACH

...

for (xml::element* employee, doc.root()->children<xml::element>())
{
	xml::element* name = (*employee)->child();
	...
}

Still, we can do better. Using XPaths. Just like the children() method of xml::element the xpath evaluation method returns all nodes, if you're only interested in xml::element nodes, you can specify this.

#include <boost/foreach.hpp>
#define foreach BOOST_FOREACH

...

xpath p("//employee/name");

for (xml::element* name, p.evaluate<xml::element>(doc))
{
    cout << "Name: " << name->content() << endl;
}

The find method of xml::element is a shortcut to xpath::evaluate(), it always returns xml::element nodes.

for (xml::element* name = doc.find("//employee/name"))
    cout << "Name: " << name->content() << endl;

The XPath implementation in libzeep implements the full XPath 1.0 specification. That means you can also do this:

// Fetch Jack's record by name:
xml::element* jack = doc.find_first("//employee[name = 'Jack Daniels']");

// or by id:
jack = doc.find_first("//employee[@id='2']");

// or by position in the list, jack is always last...
jack = doc.find_first("//employee[position() = last()]");

You can even use variables:

// Fetch Jack's record by name:
xml::context context;
context.set("var", "Jack Daniels");
xml::xpath xpath("//employee[name = $var]");

xml::element* jack = xpath.evaluate(*doc.root(), context).front();

SOAP server

This comes from the man page for the 1.0 version of libzeep. I'll have to improve this one day:

libzeep(3)                        subroutine                        libzeep(3)



NAME
       libzeep - A C++ library to create SOAP servers

SYNOPSIS
       #include <zeep/server.hpp>

       class my_server : public zeep::server
       {
         public:
           my_server(const char* addr, short port);
           void sum(int a, int b, int& c) { c = a + b; }
       };

       my_server::my_server(const char* addr, short port)
         : zeep::server("http//www.acme.org/...", addr, port)
       {
         const char kSumParameterNames[] = { "a", "b", "c" };
         register_action("sum", this, &my_server::sum, kSumParameterNames);
       }

        ...

       my_server server("0.0.0.0", 10333);
       boost::thread t(boost::bind(&my_server::run, &server));
       // and wait for a signal to stop using e.g. sigwait(3)


DESCRIPTION
       Using   libzeep   you  can  create  a  SOAP  server  by  deriving  from
       zeep::server.  In the constructor of your server,  you  call  the  base
       class  constructor  with three arguments: ns, a namespace for your SOAP
       server, address, the address to listen to, usually "0.0.0.0" to  listen
       to all available addresses. And port, the port number to bind to.

       SOAP actions are simply members of the server object and are registered
       using the register_action member  function  of  the  zeep::server  base
       class.  After  initializing the server object, the run member is called
       and the server then starts listening to the address and port specified.

       The resulting web service application will  process  incoming  request.
       There  are  three kinds of requests, the server can return an automati-
       cally generated WSDL, it can process standard SOAP message send in SOAP
       envelopes  and  it  can  handle REST style requests which are mapped to
       corresponding SOAP messages internally.

       The signature of the registered actions are used to  generate  all  the
       code needed to serialize and deserialize SOAP envelopes and to create a
       corresponding WSDL file. The signature can be as simple as the  example
       above but can also be as complex as in this one:

         void myAction(
                const std::vector<MyStructIn>& input,
                MyStructOut& output);

       In  order to make this work, you have to notify the library of the map
       ping  of  your   structure   type   to   a   name   using   the   macro
       SOAP_XML_SET_STRUCT_NAME like this:

                SOAP_XML_SET_STRUCT_NAME(MyStructIn);
                SOAP_XML_SET_STRUCT_NAME(MyStructOut);

       Next  to  this,  you have to provide a way to serialize and deserialize
       your structure. For this,  libzeep  uses  the  same  mechanism  as  the
       Boost::serialize  library, which means you have to add a templated mem
       ber function called serialize to your structure. The result  will  look
       like this:

       struct MyStructIn {
         string myField1;
         int myField2;

         template<class Archive>
         void serialize(Archive& ar, const unsigned int version)
         {
           ar & BOOST_SERIALIZATION_NVP(myField1)
              & BOOST_SERIALIZATION_NVP(myField2);
         }
       };

       Similarly  you  can  use  enum's in an action signature or as structure
       member variables. Again we need to tell the library the type  name  for
       the   enum  and  the  possible  enum  values.  We  do  this  using  the
       SOAP_XML_ADD_ENUM macro, like this:

          enum MyEnum { "one", "two" };
          SOAP_XML_ADD_ENUM(myEnum, one);
          SOAP_XML_ADD_ENUM(myEnum, two);

       As shown above, you can also use std::vector containers in  the  signa-
       ture  of  actions.  Support for other STL containers is not implemented
       yet.

       If the address used by clients of your server  is  different  from  the
       address  of  your  local  machine  (which can happen if you're behind a
       reverse proxy e.g.) you can specify the location using the set_location
       member  function  of  zeep::server.  The specified address will then be
       used in the WSDL file.


BUGS
       Undoubtedly libzeep will contain bugs. One of the more obvious  one  is
       the  missing  support  for  signatures  using STL containers other than
       std::vector.



version 1.0                       12-jan-2009                       libzeep(3)

Reference

Well, read the code for now. If I can find the time I will do better.