Libzeep comes with a validating XML parser. Using this parser is as simple as writing:
#include <zeep/xml/document.hpp>
#include <fstream>
int main()
{
std::ifstream file("test.xml");
zeep::xml::document
doc(file);
...
}
This will parse the file text.xml
and create an object
doc
containing the DOM tree.
To traverse this tree you can use the various member functions of doc which
derives from the generic zeep::xml::container
class. Siblings in the DOM tree are stored as linked lists and some elements
can have children. To make life easier, you can iterate over elements using
STL iterators.
Suppose our test.xml
file contains the following XML:
<persons> <person> <firstname>John</firstname> <lastname>Doe</lastname> </person> <person> <firstname>Jane</firstname> <lastname>Jones</lastname> </person> </persons>
You could print out the file like this:
// a document contains at most one child nodezeep::xml::element
* persons = doc.child(); // begin/end will return iterators to elements for (zeep::xml::container
::iterator person = persons->begin(); person != persons->end(); ++person) { for (zeep::xml::container
::iterator name = (*person)->begin(); name != (*person)->end(); ++name) std::cout << (*name)->name() << " = " << (*name)->content() << std::endl; }
Of course, using the new for loop construct, this code would be much more readable:
for (auto person : *persons) { for (auto name : *person) std::cout << name->name() << " = " << name->content() << std::endl; }
But if your compiler does not support that syntax, you can always use boost::range
instead:
BOOST_FOREACH (zeep::xml::element
* person, *persons) { BOOST_FOREACH (zeep::xml::element
* name, *person) std::cout << name->name() << " = " << name->content() << std::endl; }
Accessing attributes is done using the member function element::get_attribute()
.
An alternative way to read/write XML files is using serialization. To do
this, we first construct a structure called Person. We add a templated function
to this struct just like in boost::serialize
and then we can read the file.
#include <zeep/xml/document.hpp> #include <zeep/xml/serialize.hpp> #include <vector> #include <fstream> struct Person { std::string firstname; std::string lastname; template<class Archive> void serialize(Archive& ar, const unsigned int version) { ar & BOOST_SERIALIZATION_NVP(firstname) & BOOST_SERIALIZATION_NVP(lastname); } }; int main() { std::ifstream file("test.xml");zeep::xml::document
doc(file);zeep::xml::deserializer
ds(doc.child()); std::vector<Person> person; // the variable name person must be the same as the name of the XML element person ds & BOOST_SERIALIZATION_NVP(person); // alternative is to use which allows another variable name: // ds & boost::serialization::make_nvp("person", person); }
Since libzeep 3.0 we can reduce the code required.
int main()
{
std::ifstream file("test.xml");
zeep::xml::document
doc(file);
std::vector<Person> persons;
// New way of deserializing persons
doc.deserialize("persons", persons);
}
And to write out the persons, we do something similar.
zeep::xml::document
doc;
doc.serialize("persons", persons);
std::ofstream file("test-out.xml");
file << doc;
To find out more about serialization, look at the reference for zeep::xml::serializer
Libzeep comes with a XPath 1.0 implementation. You can use this to locate elements in a DOM tree easily. For a complete description of the XPath specification you should read the documentation at e.g. http://www.w3.org/TR/xpath/ or http://www.w3schools.com/xpath/default.asp.
The way it works in libzeep is that you can call find()
on an zeep::xml::element
object and it will return a zeep::xml::element_set object which is actually
a std::list
of zeep::xml::element
pointers of the elements that conform to the specification in XPath passed
as parameter to find()
.
An alternative method find_first()
can be used to return only the first element.
An example where we look for the first person in our test file with the lastname Jones:
zeep::xml::element
* jones = doc.child()->find_first("//person[lastname='Jones']");
Creating a HTTP server with libzeep is as simple as:
#include <zeep/http/server.hpp> class my_server : publiczeep::http::server
{ virtual void handle_request(constzeep::http::request
& req,zeep::http::reply
& rep) { ... // do something useful } }; int main() { my_server server; server.bind("0.0.0.0", 80); server.run(1); }
Of course you will have to fill in the handle_request
part...
Setting up a SOAP server is very easy. Let's continue with our test file and serve it as a SOAP/REST server. We already created the Person struct. The most simple server we can create is one that lists all persons in the test file:
#include <zeep/server.hpp> #include <fstream> using namespace std; ... // define the Person struct as above class my_server : publiczeep::server
{ public: my_server(); // The method we want to export void ListPersons(vector<Person>& result); }; void my_server::ListPersons(vector<Person>& result) { std::ifstream file("test.xml");zeep::xml::document
doc(file);zeep::xml::deserializer
ds(doc.child()); ds & boost::serialization::make_nvp("person", result); } my_server::my_server() :zeep::server
("http://www.example.org/soaptest", "soaptest") { // assign a name to the Person struct (will appear in the WSDL e.g.) zeep::xml::serialize_struct<Person>::set_struct_name("person"); // assign names to the parameters of the exported method, in this case there's only // one return value to name const char* kListPersonsParameterNames[] = { "response" }; register_action("ListPersons", this, &my_server::ListPersons, kListPersonsParameterNames); } int main() { my_server server; server.bind("192.168.0.1", 8083); server.run(1); // keep our server single threaded }
After building this server and running it, you can access the REST version of this routine at http://192.168.0.1:8083/rest/ListPersons and there's a WSDL at http://192.168.0.1:8083/wsdl