aboutsummaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2009-10-13 11:29:26 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2009-10-13 11:29:26 +0200
commitafebd79d44b75aed3b38e867c65330ba80ddc0ee (patch)
tree7fc95caae113884defce1ac712f93327f58f72e0 /examples
parent0b21758fcd9f3127e30a1c9172c4c27b46a2b957 (diff)
Extended the streaming example
It now shows how to perform stream-oriented, partially in-memory XML processing using the C++/Tree mapping.
Diffstat (limited to 'examples')
-rw-r--r--examples/cxx/tree/README11
-rw-r--r--examples/cxx/tree/performance/serialization.cxx2
-rw-r--r--examples/cxx/tree/streaming/README50
-rw-r--r--examples/cxx/tree/streaming/driver.cxx173
-rw-r--r--examples/cxx/tree/streaming/makefile14
-rw-r--r--examples/cxx/tree/streaming/parser.cxx288
-rw-r--r--examples/cxx/tree/streaming/parser.hxx46
-rw-r--r--examples/cxx/tree/streaming/position.xml30
-rw-r--r--examples/cxx/tree/streaming/position.xsd38
-rw-r--r--examples/cxx/tree/streaming/records.xsd37
-rw-r--r--examples/cxx/tree/streaming/serializer.cxx165
-rw-r--r--examples/cxx/tree/streaming/serializer.hxx81
12 files changed, 843 insertions, 92 deletions
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<xml_schema::parsing> ();
+ eh.throw_if_failed<xml_schema::serialization> ();
}
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 <iostream>
#include <fstream>
-#include "records.hxx"
+#include <xercesc/dom/DOM.hpp>
-using std::cerr;
-using std::endl;
-using std::ios_base;
+#include <xsd/cxx/xml/string.hxx> // 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<DOMDocument> 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 << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << endl
- << "<records>" << endl;
+ << "<op:object xmlns:op=\"http://www.codesynthesis.com/op\"" << endl
+ << " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"" << endl
+ << " xsi:schemaLocation=\"http://www.codesynthesis.com/op " <<
+ "position.xsd\"" << endl
+ << " id=\"" << 123 << "\">" << 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
+ << "</op:object>" << endl;
- ofs << "</records>" << 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 <boris@codesynthesis.com>
+// copyright : not copyrighted - public domain
+
+#include <xercesc/util/XMLUni.hpp>
+#include <xercesc/util/XMLString.hpp>
+
+#include <xercesc/sax2/Attributes.hpp>
+#include <xercesc/sax2/DefaultHandler.hpp>
+#include <xercesc/sax2/SAX2XMLReader.hpp>
+#include <xercesc/sax2/XMLReaderFactory.hpp>
+
+#include <xercesc/dom/DOM.hpp>
+
+#if _XERCES_VERSION >= 30000
+# include <xercesc/dom/impl/DOMTextImpl.hpp>
+#endif
+
+#include <xsd/cxx/auto-array.hxx>
+
+#include <xsd/cxx/xml/sax/std-input-source.hxx>
+#include <xsd/cxx/xml/sax/bits/error-handler-proxy.hxx>
+
+#include <xsd/cxx/tree/exceptions.hxx>
+#include <xsd/cxx/tree/error-handler.hxx>
+
+#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<DOMDocument>
+ start (istream& is, const string& id, bool validate);
+
+ xml::dom::auto_ptr<DOMDocument>
+ 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<SAX2XMLReader> parser_;
+ XMLPScanToken token_;
+ tree::error_handler<char> error_handler_;
+ xml::sax::bits::error_handler_proxy<char> error_proxy_;
+ auto_ptr<xml::sax::std_input_source> isrc_;
+
+ size_t depth_;
+
+ // DOM document being built.
+ //
+ DOMImplementation& dom_impl_;
+ xml::dom::auto_ptr<DOMDocument> 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<DOMDocument> 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<tree::parsing<char> > ();
+
+ while (r && depth_ == 0)
+ {
+ r = parser_->parseNext (token_);
+ error_handler_.throw_if_failed<tree::parsing<char> > ();
+ }
+
+ if (!r)
+ return xml::dom::auto_ptr<DOMDocument> (0);
+
+ return doc_;
+}
+
+xml::dom::auto_ptr<DOMDocument> parser_impl::
+next ()
+{
+ // We should be at depth 1. If not, then we are done parsing.
+ //
+ if (depth_ != 1)
+ return xml::dom::auto_ptr<DOMDocument> (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<tree::parsing<char> > ();
+ }
+
+ if (!r)
+ return xml::dom::auto_ptr<DOMDocument> (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<tree::parsing<char> > ();
+ }
+
+ if (!r)
+ return xml::dom::auto_ptr<DOMDocument> (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<DOMElement*> (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<DOMTextImpl*> (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<XMLCh> 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<DOMDocument> parser::
+start (istream& is, const string& id, bool val)
+{
+ return impl_->start (is, id, val);
+}
+
+xml::dom::auto_ptr<DOMDocument> 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 <boris@codesynthesis.com>
+// copyright : not copyrighted - public domain
+
+#ifndef PARSER_HXX
+#define PARSER_HXX
+
+#include <string>
+#include <iosfwd>
+#include <memory> // std::auto_ptr
+
+#include <xercesc/dom/DOMDocument.hpp>
+
+#include <xsd/cxx/xml/dom/auto-ptr.hxx>
+
+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<xercesc::DOMDocument>
+ 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<xercesc::DOMDocument>
+ next ();
+
+private:
+ parser (const parser&);
+
+ parser&
+ operator= (const parser&);
+
+private:
+ std::auto_ptr<parser_impl> 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 @@
+<?xml version="1.0"?>
+
+<!--
+
+file : examples/cxx/tree/streaming/position.xml
+author : Boris Kolpackov <boris@codesynthesis.com>
+copyright : not copyrighted - public domain
+
+-->
+
+<op:object xmlns:op="http://www.codesynthesis.com/op"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.codesynthesis.com/op position.xsd"
+ id="123">
+
+ <header>
+ <name>Lion's Head</name>
+ <type>rock</type>
+ </header>
+
+ <position lat="-33.8569" lon="18.5083"/>
+ <position lat="-33.8568" lon="18.5083"/>
+ <position lat="-33.8568" lon="18.5082"/>
+ <position lat="-33.8570" lon="18.5083"/>
+ <position lat="-33.8569" lon="18.5084"/>
+ <position lat="-33.8570" lon="18.5084"/>
+ <position lat="-33.8570" lon="18.5082"/>
+ <position lat="-33.8569" lon="18.5082"/>
+
+</op:object>
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 @@
+<?xml version="1.0"?>
+
+<!--
+
+file : examples/cxx/tree/streaming/position.xsd
+author : Boris Kolpackov <boris@codesynthesis.com>
+copyright : not copyrighted - public domain
+
+-->
+
+<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:op="http://www.codesynthesis.com/op"
+ targetNamespace="http://www.codesynthesis.com/op">
+
+
+ <xsd:complexType name="header">
+ <xsd:sequence>
+ <xsd:element name="name" type="xsd:string"/>
+ <xsd:element name="type" type="xsd:string"/>
+ </xsd:sequence>
+ </xsd:complexType>
+
+ <xsd:complexType name="position">
+ <xsd:attribute name="lat" type="xsd:float" use="required"/>
+ <xsd:attribute name="lon" type="xsd:float" use="required"/>
+ </xsd:complexType>
+
+ <xsd:complexType name="object">
+ <xsd:sequence>
+ <xsd:element name="header" type="op:header"/>
+ <xsd:element name="position" type="op:position" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ <xsd:attribute name="id" type="xsd:unsignedInt" use="required"/>
+ </xsd:complexType>
+
+ <xsd:element name="object" type="op:object"/>
+
+</xsd:schema>
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 @@
-<?xml version="1.0"?>
-
-<!--
-
-file : examples/cxx/tree/streaming/records.xsd
-author : Boris Kolpackov <boris@codesynthesis.com>
-copyright : not copyrighted - public domain
-
--->
-<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
-
- <xsd:complexType name="record">
- <xsd:sequence>
- <xsd:element name="data" type="xsd:string"/>
- </xsd:sequence>
- </xsd:complexType>
-
- <!--
-
- We need this global element so that we get serialization functions
- for record. Note that global elements are always qualified. As a
- result, if you are using XML namespaces (this example is not), you
- will need to make sure the record element inside the records type
- is also qualified.
-
- -->
- <xsd:element name="record" type="record"/>
-
- <xsd:complexType name="records">
- <xsd:sequence>
- <xsd:element name="record" type="record" maxOccurs="unbounded"/>
- </xsd:sequence>
- </xsd:complexType>
-
- <xsd:element name="records" type="records"/>
-
-</xsd:schema>
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 <boris@codesynthesis.com>
+// copyright : not copyrighted - public domain
+
+#include <xercesc/util/XMLUni.hpp>
+
+#include <xercesc/dom/DOM.hpp>
+
+#include <xsd/cxx/xml/string.hxx>
+#include <xsd/cxx/xml/dom/bits/error-handler-proxy.hxx>
+#include <xsd/cxx/xml/dom/serialization-source.hxx>
+
+#include <xsd/cxx/tree/exceptions.hxx>
+#include <xsd/cxx/tree/error-handler.hxx>
+
+#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<DOMLSOutput> out_;
+ xml::dom::auto_ptr<DOMLSSerializer> serializer_;
+#else
+ xml::dom::auto_ptr<DOMWriter> serializer_;
+#endif
+
+ auto_ptr<xml::dom::ostream_format_target> oft_;
+
+ tree::error_handler<char> error_handler_;
+ xml::dom::bits::error_handler_proxy<char> error_proxy_;
+
+ // DOM document that we use to create the elements.
+ //
+ DOMImplementation& dom_impl_;
+ xml::dom::auto_ptr<DOMDocument> 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<tree::serialization<char> > ();
+}
+
+//
+// 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 <boris@codesynthesis.com>
+// copyright : not copyrighted - public domain
+
+#ifndef SERIALIZER_HXX
+#define SERIALIZER_HXX
+
+#include <string>
+#include <iosfwd>
+#include <memory> // std::auto_ptr
+
+#include <xercesc/dom/DOMElement.hpp>
+
+#include <xsd/cxx/xml/dom/auto-ptr.hxx>
+
+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 <typename T>
+ 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 <typename T>
+ 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<serializer_impl> impl_;
+};
+
+template <typename T>
+inline void serializer::
+next (const std::string& name, const T& x)
+{
+ xsd::cxx::xml::dom::auto_ptr<xercesc::DOMElement> e (create (name));
+ *e << x;
+ serialize (*e);
+}
+
+template <typename T>
+inline void serializer::
+next (const std::string& ns, const std::string& name, const T& x)
+{
+ xsd::cxx::xml::dom::auto_ptr<xercesc::DOMElement> e (create (ns, name));
+ *e << x;
+ serialize (*e);
+}
+
+#endif // SERIALIZER_HXX