From afebd79d44b75aed3b38e867c65330ba80ddc0ee Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 13 Oct 2009 11:29:26 +0200 Subject: Extended the streaming example It now shows how to perform stream-oriented, partially in-memory XML processing using the C++/Tree mapping. --- examples/cxx/tree/README | 11 +- examples/cxx/tree/performance/serialization.cxx | 2 +- examples/cxx/tree/streaming/README | 50 ++-- examples/cxx/tree/streaming/driver.cxx | 173 +++++++++++--- examples/cxx/tree/streaming/makefile | 14 +- examples/cxx/tree/streaming/parser.cxx | 288 ++++++++++++++++++++++++ examples/cxx/tree/streaming/parser.hxx | 46 ++++ examples/cxx/tree/streaming/position.xml | 30 +++ examples/cxx/tree/streaming/position.xsd | 38 ++++ examples/cxx/tree/streaming/records.xsd | 37 --- examples/cxx/tree/streaming/serializer.cxx | 165 ++++++++++++++ examples/cxx/tree/streaming/serializer.hxx | 81 +++++++ 12 files changed, 843 insertions(+), 92 deletions(-) create mode 100644 examples/cxx/tree/streaming/parser.cxx create mode 100644 examples/cxx/tree/streaming/parser.hxx create mode 100644 examples/cxx/tree/streaming/position.xml create mode 100644 examples/cxx/tree/streaming/position.xsd delete mode 100644 examples/cxx/tree/streaming/records.xsd create mode 100644 examples/cxx/tree/streaming/serializer.cxx create mode 100644 examples/cxx/tree/streaming/serializer.hxx (limited to 'examples') diff --git a/examples/cxx/tree/README b/examples/cxx/tree/README index 16e5ce3..b6993d9 100644 --- a/examples/cxx/tree/README +++ b/examples/cxx/tree/README @@ -55,11 +55,12 @@ custom/ overview of each example in this directory. streaming - Shows how to create an XML document by performing multiple - serializations of its smaller parts. This can be useful when the - document is too large to fit into memory or when the other end - needs to start processing without waiting for the whole document - (streaming). + Shows how to perform stream-oriented, partially in-memory XML + processing using the C++/Tree mapping. With the partially in-memory + parsing and serialization only a part of the object model is in + memory at any given time. With this approach we can process parts + of the document as they become available as well as handle documents + that are too large to fit into memory. binary/ A collection of examples that show how to serialize the object model diff --git a/examples/cxx/tree/performance/serialization.cxx b/examples/cxx/tree/performance/serialization.cxx index 5ad6654..f888d40 100644 --- a/examples/cxx/tree/performance/serialization.cxx +++ b/examples/cxx/tree/performance/serialization.cxx @@ -116,7 +116,7 @@ serialization (const char* file, unsigned long iter) writer->writeNode (&ft, *doc); #endif - eh.throw_if_failed (); + eh.throw_if_failed (); } os::time end; diff --git a/examples/cxx/tree/streaming/README b/examples/cxx/tree/streaming/README index 51e5c14..ac7e7f0 100644 --- a/examples/cxx/tree/streaming/README +++ b/examples/cxx/tree/streaming/README @@ -1,22 +1,46 @@ -This example shows how to create an XML document by performing multiple -serializations of its smaller parts. This can be useful when the -document is too large to fit into memory or when the other end needs -to start processing without waiting for the whole document (streaming). +This example shows how to perform stream-oriented, partially in-memory +XML processing using the C++/Tree mapping. With the partially in-memory +parsing and serialization only a part of the object model is in memory at +any given time. With this approach we can process parts of the document +as they become available as well as handle documents that are too large +to fit into memory. The example consists of the following files: -records.xsd - XML Schema which describes a collection of data records. +position.xsd + XML Schema which describes a simple object position vocabulary. The + position is represented as a potentially large series of latitude and + longitude measurements. -records.hxx -records.cxx - C++ types that represent the given vocabulary as well as serialization - functions. These are generated by XSD from records.xsd. +position.xml + Sample object position document. + +position.hxx +position.cxx + C++ types that represent the position vocabulary as well as parsing + and serialization functions. These are generated by XSD from + position.xsd. + +parser.hxx +parser.cxx + Stream-oriented DOM parser implementation that is built on top of the + Xerces-C++ SAX2 parser in the progressive parsing mode. This parser + allows us to parse an XML document as a series of DOM fragments. + +serializer.hxx +serializer.cxx + Stream-oriented DOM serializer implementation that allows us to + serialize an XML Document as a series of object model fragments. driver.cxx - Driver for the example. It progressively serializes one thousand data - record into a file (out.xml). + Driver for the example. It first parses the input file into a series + of DOM fragments which are then parsed into the object model fragments. + The driver prints the information from the document as it becomes + available. The driver then creates a new XML document (out.xml) by + creating and serializing a series of object model fragments. To run the example simply execute: -$ ./driver +$ ./driver position.xml + +The serialization results are written to the out.xml file. diff --git a/examples/cxx/tree/streaming/driver.cxx b/examples/cxx/tree/streaming/driver.cxx index 6afc1ce..6c6fd51 100644 --- a/examples/cxx/tree/streaming/driver.cxx +++ b/examples/cxx/tree/streaming/driver.cxx @@ -5,61 +5,172 @@ #include #include -#include "records.hxx" +#include -using std::cerr; -using std::endl; -using std::ios_base; +#include // xml::string + +#include "parser.hxx" +#include "serializer.hxx" +#include "position.hxx" + +using namespace std; +using namespace xercesc; + +static void +measure_position (unsigned int n, float& lat, float& lon); int -main () +main (int argc, char* argv[]) { + if (argc != 2) + { + cerr << "usage: " << argv[0] << " position.xml" << endl; + return 1; + } + + int r (0); + + // We need to initialize the Xerces-C++ runtime because we are doing + // the XML-to-DOM parsing ourselves. + // + xercesc::XMLPlatformUtils::Initialize (); + try { - std::ofstream ofs; + using namespace op; + namespace xml = xsd::cxx::xml; + + // Parse. + // + + ifstream ifs; + ifs.exceptions (ifstream::badbit | ifstream::failbit); + ifs.open (argv[1]); + + parser p; + + // The first document we get is the "carcase" of the complete document. + // That is, the root element with all the attributes but without any + // content. We may need it to get to the attributes in the root element. + // + // There are two ways this can be done. The easiest approach is to + // instantiate the root element's type (object in our case). This + // will only work if all the content in the root element is optional. + // Alternatively, we can manually look up attributes that we are + // interested in and instantiate the corresponding type. The following + // fragment shows how to use the second approach. + // + xml_schema::dom::auto_ptr doc (p.start (ifs, argv[1], true)); + + // Find the id attribute. + // + DOMAttr* id_attr ( + doc->getDocumentElement ()->getAttributeNode ( + xml::string ("id").c_str ())); + + // Use the type and traits aliases from the object model. + // + object::id_type id (object::id_traits::create (*id_attr, 0, 0)); + cerr << "id: " << id << endl; + + // The next chunk we get is the header element. + // + doc = p.next (); + header hdr (*doc->getDocumentElement ()); + cerr << "name: " << hdr.name () << endl + << "type: " << hdr.type () << endl; + + // The rest is position elements. + // + for (doc = p.next (); doc.get () != 0; doc = p.next ()) + { + position p (*doc->getDocumentElement ()); + cerr << "lat: " << p.lat () << " lon: " << p.lon () << endl; + } + + // Serialize. + // + + ofstream ofs; ofs.exceptions (ios_base::badbit | ios_base::failbit); ofs.open ("out.xml"); - // We will need to create XML declaration as well as open and close - // the root tag ourselves. + serializer s; + + // With this approach we manually write the XML declaration, opening + // and closing root element tags, as well as any attributes in the + // root element. // ofs << "" << endl - << "" << endl; + << "" << endl; - // For performance reasons, we would like to initialize/terminate - // Xerces-C++ ourselves once instead of letting the serialization - // function do it for every record. - // - xercesc::XMLPlatformUtils::Initialize (); + s.start (ofs); - xml_schema::namespace_infomap map; + // Serialize the header. + // + header h ("Lion's Head", "rock"); + s.next ("header", h); - for (unsigned long i (0); i < 1000; ++i) + // Serialize position elements, one at a time. + // + for (unsigned short i (0); i < 8; i++) { - // Create the next record. - // - record r ("data"); - - record_ (ofs, - r, - map, - "UTF-8", - xml_schema::flags::dont_initialize | - xml_schema::flags::no_xml_declaration); + float lat, lon; + measure_position (i, lat, lon); + position p (lat, lon); + s.next ("position", p); } - xercesc::XMLPlatformUtils::Terminate (); + // Close the root element. + // + ofs << endl + << "" << endl; - ofs << "" << endl; } catch (const xml_schema::exception& e) { cerr << e << endl; - return 1; + r = 1; } - catch (const std::ios_base::failure&) + catch (const ios_base::failure&) { cerr << "io failure" << endl; - return 1; + r = 1; } + + xercesc::XMLPlatformUtils::Terminate (); + return r; +} + +// Position measurement instrument interface. +// +struct measurements +{ + float lat; + float lon; +}; + +measurements test_measurements [8] = +{ + {-33.8569F, 18.5083F}, + {-33.8568F, 18.5083F}, + {-33.8568F, 18.5082F}, + {-33.8570F, 18.5083F}, + {-33.8569F, 18.5084F}, + {-33.8570F, 18.5084F}, + {-33.8570F, 18.5082F}, + {-33.8569F, 18.5082F} +}; + +static void +measure_position (unsigned int n, float& lat, float& lon) +{ + // Call the instrument to measure the position. + // + lat = test_measurements[n].lat; + lon = test_measurements[n].lon; } diff --git a/examples/cxx/tree/streaming/makefile b/examples/cxx/tree/streaming/makefile index ddac169..2cd388c 100644 --- a/examples/cxx/tree/streaming/makefile +++ b/examples/cxx/tree/streaming/makefile @@ -5,8 +5,8 @@ include $(dir $(lastword $(MAKEFILE_LIST)))../../../../build/bootstrap.make -xsd := records.xsd -cxx := driver.cxx +xsd := position.xsd +cxx := driver.cxx parser.cxx serializer.cxx obj := $(addprefix $(out_base)/,$(cxx:.cxx=.o) $(xsd:.xsd=.o)) dep := $(obj:.o=.o.d) @@ -35,8 +35,7 @@ genf := $(xsd:.xsd=.hxx) $(xsd:.xsd=.ixx) $(xsd:.xsd=.cxx) gen := $(addprefix $(out_base)/,$(genf)) $(gen): xsd := $(out_root)/xsd/xsd -$(gen): xsd_options := --generate-serialization --suppress-parsing \ ---root-element-all +$(gen): xsd_options := --generate-serialization $(gen): $(out_root)/xsd/xsd $(call include-dep,$(dep)) @@ -53,7 +52,12 @@ $(dist) $(dist-win) $(dist-common): path := $(subst $(src_root)/,,$(src_base)) $(dist-common): $(call install-data,$(src_base)/driver.cxx,$(dist_prefix)/$(path)/driver.cxx) - $(call install-data,$(src_base)/records.xsd,$(dist_prefix)/$(path)/records.xsd) + $(call install-data,$(src_base)/parser.cxx,$(dist_prefix)/$(path)/parser.cxx) + $(call install-data,$(src_base)/parser.hxx,$(dist_prefix)/$(path)/parser.hxx) + $(call install-data,$(src_base)/serializer.cxx,$(dist_prefix)/$(path)/serializer.cxx) + $(call install-data,$(src_base)/serializer.hxx,$(dist_prefix)/$(path)/serializer.hxx) + $(call install-data,$(src_base)/position.xsd,$(dist_prefix)/$(path)/position.xsd) + $(call install-data,$(src_base)/position.xml,$(dist_prefix)/$(path)/position.xml) $(dist): $(dist-common) $(call install-data,$(src_base)/README,$(dist_prefix)/$(path)/README) diff --git a/examples/cxx/tree/streaming/parser.cxx b/examples/cxx/tree/streaming/parser.cxx new file mode 100644 index 0000000..1a0fa73 --- /dev/null +++ b/examples/cxx/tree/streaming/parser.cxx @@ -0,0 +1,288 @@ +// file : examples/cxx/tree/streaming/parser.cxx +// author : Boris Kolpackov +// copyright : not copyrighted - public domain + +#include +#include + +#include +#include +#include +#include + +#include + +#if _XERCES_VERSION >= 30000 +# include +#endif + +#include + +#include +#include + +#include +#include + +#include "parser.hxx" + +using namespace std; +using namespace xercesc; + +namespace xml = xsd::cxx::xml; +namespace tree = xsd::cxx::tree; + +class parser_impl: DefaultHandler +{ +public: + parser_impl (); + + xml::dom::auto_ptr + start (istream& is, const string& id, bool validate); + + xml::dom::auto_ptr + next (); + + // SAX event handlers. + // +private: + virtual void + startElement (const XMLCh* const uri, + const XMLCh* const lname, + const XMLCh* const qname, + const Attributes& attributes); + + virtual void + endElement (const XMLCh* const uri, + const XMLCh* const lname, + const XMLCh* const qname); + + virtual void + characters (const XMLCh* const s, +#if _XERCES_VERSION >= 30000 + const XMLSize_t length +#else + const unsigned int length +#endif + ); + +private: + // SAX parser. + // + bool clean_; + auto_ptr parser_; + XMLPScanToken token_; + tree::error_handler error_handler_; + xml::sax::bits::error_handler_proxy error_proxy_; + auto_ptr isrc_; + + size_t depth_; + + // DOM document being built. + // + DOMImplementation& dom_impl_; + xml::dom::auto_ptr doc_; + DOMElement* cur_; +}; + +const XMLCh ls[] = {chLatin_L, chLatin_S, chNull}; + +parser_impl:: +parser_impl () + : clean_ (true), + parser_ (XMLReaderFactory::createXMLReader ()), + error_proxy_ (error_handler_), + dom_impl_ (*DOMImplementationRegistry::getDOMImplementation (ls)) +{ + parser_->setFeature (XMLUni::fgSAX2CoreNameSpaces, true); + parser_->setFeature (XMLUni::fgSAX2CoreNameSpacePrefixes, true); + parser_->setFeature (XMLUni::fgXercesValidationErrorAsFatal, true); + parser_->setFeature (XMLUni::fgXercesSchemaFullChecking, false); + + parser_->setErrorHandler (&error_proxy_); + parser_->setContentHandler (this); +} + +xml::dom::auto_ptr parser_impl:: +start (istream& is, const string& id, bool val) +{ + // Reset our state. + // + depth_ = 0; + doc_.reset (); + error_handler_.reset (); + + if (!clean_) + parser_->parseReset (token_); + else + clean_ = false; + + isrc_.reset (new xml::sax::std_input_source (is, id)); + + parser_->setFeature (XMLUni::fgSAX2CoreValidation, val); + parser_->setFeature (XMLUni::fgXercesSchema, val); + + // Start parsing. The first document that we return is a "carcase" + // of the complete document. That is, the root element with all the + // attributes but without any content. + // + bool r (parser_->parseFirst (*isrc_, token_)); + error_handler_.throw_if_failed > (); + + while (r && depth_ == 0) + { + r = parser_->parseNext (token_); + error_handler_.throw_if_failed > (); + } + + if (!r) + return xml::dom::auto_ptr (0); + + return doc_; +} + +xml::dom::auto_ptr parser_impl:: +next () +{ + // We should be at depth 1. If not, then we are done parsing. + // + if (depth_ != 1) + return xml::dom::auto_ptr (0); + + bool r (true); + + // Keep calling parseNext() until we either move to a greater depth or + // get a document. This way we skip the text (presumably whitespaces) + // that may be preceding the next chunk. + // + while (r && depth_ == 1 && doc_.get () == 0) + { + parser_->parseNext (token_); + error_handler_.throw_if_failed > (); + } + + if (!r) + return xml::dom::auto_ptr (0); + + // If we are not at depth 1, keep calling parseNext() until we get + // there. + // + while (r && depth_ != 1) + { + r = parser_->parseNext (token_); + error_handler_.throw_if_failed > (); + } + + if (!r) + return xml::dom::auto_ptr (0); + + return doc_; +} + +// DOM builder. +// + +void parser_impl:: +startElement (const XMLCh* const uri, + const XMLCh* const /*lname*/, + const XMLCh* const qname, + const Attributes& attr) +{ + if (doc_.get () == 0) + { + doc_.reset (dom_impl_.createDocument (uri, qname, 0)); + cur_ = doc_->getDocumentElement (); + } + else + { + DOMElement* e = doc_->createElementNS (uri, qname); + cur_->appendChild (e); + cur_ = e; + } + + // Set attributes. + // +#if _XERCES_VERSION >= 30000 + for (XMLSize_t i (0), end (attr.getLength()); i < end; ++i) +#else + for (unsigned int i (0), end (attr.getLength()); i < end; ++i) +#endif + { + cur_->setAttributeNS (attr.getURI (i), + attr.getQName (i), + attr.getValue (i)); + } + + depth_++; +} + +void parser_impl:: +endElement (const XMLCh* const /*uri*/, + const XMLCh* const /*lname*/, + const XMLCh* const /*qname*/) +{ + // We have an element parent only on depth 2 or greater. + // + if (--depth_ > 1) + cur_ = static_cast (cur_->getParentNode ()); +} + +#if _XERCES_VERSION >= 30000 +void parser_impl:: +characters (const XMLCh* const s, const XMLSize_t length) +{ + const XMLCh empty[] = {chNull}; + + // Ignore text content (presumably whitespaces) in the root element. + // + if (depth_ > 1) + { + DOMText* t = doc_->createTextNode (empty); + static_cast (t)->appendData (s, length); + cur_->appendChild (t); + } +} +#else +void parser_impl:: +characters (const XMLCh* const s, const unsigned int length) +{ + // Ignore text content (presumably whitespaces) in the root element. + // + if (depth_ > 1) + { + // For Xerces-C++ 2-series we have to make copy. + // + xsd::cxx::auto_array tmp (new XMLCh[length + 1]); + XMLString::copyNString (tmp.get (), s, length); + cur_->appendChild (doc_->createTextNode (tmp.get ())); + } +} +#endif + + +// +// parser +// + +parser:: +~parser () +{ +} + +parser:: +parser () + : impl_ (new parser_impl) +{ +} + +xml::dom::auto_ptr parser:: +start (istream& is, const string& id, bool val) +{ + return impl_->start (is, id, val); +} + +xml::dom::auto_ptr parser:: +next () +{ + return impl_->next (); +} diff --git a/examples/cxx/tree/streaming/parser.hxx b/examples/cxx/tree/streaming/parser.hxx new file mode 100644 index 0000000..000d022 --- /dev/null +++ b/examples/cxx/tree/streaming/parser.hxx @@ -0,0 +1,46 @@ +// file : examples/cxx/tree/streaming/parser.hxx +// author : Boris Kolpackov +// copyright : not copyrighted - public domain + +#ifndef PARSER_HXX +#define PARSER_HXX + +#include +#include +#include // std::auto_ptr + +#include + +#include + +class parser_impl; + +class parser +{ +public: + ~parser (); + parser (); + + // The start function returns a "carcase" of the complete document. That + // is, the root element with all the attributes but without any content. + // + xsd::cxx::xml::dom::auto_ptr + start (std::istream& is, const std::string& id, bool validate); + + // The next function returns next first-level element with all its + // attributes and content or 0 if no more available. + // + xsd::cxx::xml::dom::auto_ptr + next (); + +private: + parser (const parser&); + + parser& + operator= (const parser&); + +private: + std::auto_ptr impl_; +}; + +#endif // PARSER_HXX diff --git a/examples/cxx/tree/streaming/position.xml b/examples/cxx/tree/streaming/position.xml new file mode 100644 index 0000000..e40b0c8 --- /dev/null +++ b/examples/cxx/tree/streaming/position.xml @@ -0,0 +1,30 @@ + + + + + + +
+ Lion's Head + rock +
+ + + + + + + + + + +
diff --git a/examples/cxx/tree/streaming/position.xsd b/examples/cxx/tree/streaming/position.xsd new file mode 100644 index 0000000..13f6e9a --- /dev/null +++ b/examples/cxx/tree/streaming/position.xsd @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/cxx/tree/streaming/records.xsd b/examples/cxx/tree/streaming/records.xsd deleted file mode 100644 index 2b357e6..0000000 --- a/examples/cxx/tree/streaming/records.xsd +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/cxx/tree/streaming/serializer.cxx b/examples/cxx/tree/streaming/serializer.cxx new file mode 100644 index 0000000..58bc5cb --- /dev/null +++ b/examples/cxx/tree/streaming/serializer.cxx @@ -0,0 +1,165 @@ +// file : examples/cxx/tree/streaming/serializer.cxx +// author : Boris Kolpackov +// copyright : not copyrighted - public domain + +#include + +#include + +#include +#include +#include + +#include +#include + +#include "serializer.hxx" + +using namespace std; +using namespace xercesc; + +namespace xml = xsd::cxx::xml; +namespace tree = xsd::cxx::tree; + +class serializer_impl +{ +public: + serializer_impl (); + + void + start (ostream& os, const string& encoding); + + DOMElement* + create (const string& name); + + DOMElement* + create (const string& ns, const string& qname); + + void + serialize (DOMElement& e); + +private: + // Serializer. + // +#if _XERCES_VERSION >= 30000 + xml::dom::auto_ptr out_; + xml::dom::auto_ptr serializer_; +#else + xml::dom::auto_ptr serializer_; +#endif + + auto_ptr oft_; + + tree::error_handler error_handler_; + xml::dom::bits::error_handler_proxy error_proxy_; + + // DOM document that we use to create the elements. + // + DOMImplementation& dom_impl_; + xml::dom::auto_ptr doc_; +}; + +const XMLCh ls[] = {chLatin_L, chLatin_S, chNull}; + +serializer_impl:: +serializer_impl () + : error_proxy_ (error_handler_), + dom_impl_ (*DOMImplementationRegistry::getDOMImplementation (ls)), + doc_ (dom_impl_.createDocument ()) +{ +#if _XERCES_VERSION >= 30000 + serializer_.reset (dom_impl_.createLSSerializer ()); + DOMConfiguration* conf (serializer_->getDomConfig ()); + + conf->setParameter (XMLUni::fgDOMErrorHandler, &error_proxy_); + conf->setParameter (XMLUni::fgDOMWRTDiscardDefaultContent, true); + conf->setParameter (XMLUni::fgDOMWRTFormatPrettyPrint, true); + conf->setParameter (XMLUni::fgDOMXMLDeclaration, false); +#else + serializer_.reset (dom_impl_.createDOMWriter ()); + + serializer_->setErrorHandler (&error_proxy_); + serializer_->setFeature (XMLUni::fgDOMWRTDiscardDefaultContent, true); + serializer_->setFeature (XMLUni::fgDOMWRTFormatPrettyPrint, true); + serializer_->setFeature (XMLUni::fgDOMXMLDeclaration, false); +#endif +} + +void serializer_impl:: +start (ostream& os, const string& encoding) +{ + error_handler_.reset (); + oft_.reset (new xml::dom::ostream_format_target (os)); + +#if _XERCES_VERSION >= 30000 + out_.reset (dom_impl_.createLSOutput ()); + out_->setEncoding (xml::string (encoding).c_str ()); + out_->setByteStream (oft_.get ()); +#else + serializer_->setEncoding (xml::string (encoding).c_str ()); +#endif +} + +DOMElement* serializer_impl:: +create (const string& name) +{ + return doc_->createElement (xml::string (name).c_str ()); +} + +DOMElement* serializer_impl:: +create (const string& ns, const string& qname) +{ + return doc_->createElementNS ( + xml::string (ns).c_str (), xml::string (qname).c_str ()); +} + +void serializer_impl:: +serialize (DOMElement& e) +{ +#if _XERCES_VERSION >= 30000 + serializer_->write (&e, out_.get ()); +#else + serializer_->writeNode (oft_.get (), e); +#endif + + error_handler_.throw_if_failed > (); +} + +// +// serializer +// + +serializer:: +~serializer () +{ +} + +serializer:: +serializer () + : impl_ (new serializer_impl) +{ +} + +void serializer:: +start (ostream& os, const string& encoding) +{ + impl_->start (os, encoding); +} + +DOMElement* serializer:: +create (const string& name) +{ + return impl_->create (name); +} + +DOMElement* serializer:: +create (const string& ns, const string& qname) +{ + return impl_->create (ns, qname); +} + +void serializer:: +serialize (DOMElement& e) +{ + impl_->serialize (e); +} diff --git a/examples/cxx/tree/streaming/serializer.hxx b/examples/cxx/tree/streaming/serializer.hxx new file mode 100644 index 0000000..6ff0114 --- /dev/null +++ b/examples/cxx/tree/streaming/serializer.hxx @@ -0,0 +1,81 @@ +// file : examples/cxx/tree/streaming/serializer.hxx +// author : Boris Kolpackov +// copyright : not copyrighted - public domain + +#ifndef SERIALIZER_HXX +#define SERIALIZER_HXX + +#include +#include +#include // std::auto_ptr + +#include + +#include + +class serializer_impl; + +class serializer +{ +public: + ~serializer (); + serializer (); + + // Start the serialization process. + // + void + start (std::ostream& is, const std::string& encoding = "UTF-8"); + + // Serialize next object model fragment into an element with the specified + // name. + // + template + void + next (const std::string& name, const T& x); + + // Serialize next object model fragment into an element with the specified + // namespace and qualified name. + // + template + void + next (const std::string& ns, const std::string& name, const T& x); + +private: + serializer (const serializer&); + + serializer& + operator= (const serializer&); + +private: + xercesc::DOMElement* + create (const std::string& name); + + xercesc::DOMElement* + create (const std::string& ns, const std::string& name); + + void + serialize (xercesc::DOMElement&); + +private: + std::auto_ptr impl_; +}; + +template +inline void serializer:: +next (const std::string& name, const T& x) +{ + xsd::cxx::xml::dom::auto_ptr e (create (name)); + *e << x; + serialize (*e); +} + +template +inline void serializer:: +next (const std::string& ns, const std::string& name, const T& x) +{ + xsd::cxx::xml::dom::auto_ptr e (create (ns, name)); + *e << x; + serialize (*e); +} + +#endif // SERIALIZER_HXX -- cgit v1.1