From 4a2834255dc48166afc537e9e9dce80be457fa14 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Wed, 30 Sep 2009 19:14:56 +0200 Subject: New example showing handling of mixed content with type customization --- examples/cxx/tree/custom/README | 3 + examples/cxx/tree/custom/makefile | 4 +- examples/cxx/tree/custom/mixed/README | 50 +++++++++ examples/cxx/tree/custom/mixed/driver.cxx | 124 +++++++++++++++++++++++ examples/cxx/tree/custom/mixed/makefile | 106 +++++++++++++++++++ examples/cxx/tree/custom/mixed/people-custom.cxx | 90 ++++++++++++++++ examples/cxx/tree/custom/mixed/people-custom.hxx | 84 +++++++++++++++ examples/cxx/tree/custom/mixed/people.xml | 39 +++++++ examples/cxx/tree/custom/mixed/people.xsd | 46 +++++++++ examples/cxx/tree/mixed/README | 3 + 10 files changed, 547 insertions(+), 2 deletions(-) create mode 100644 examples/cxx/tree/custom/mixed/README create mode 100644 examples/cxx/tree/custom/mixed/driver.cxx create mode 100644 examples/cxx/tree/custom/mixed/makefile create mode 100644 examples/cxx/tree/custom/mixed/people-custom.cxx create mode 100644 examples/cxx/tree/custom/mixed/people-custom.hxx create mode 100644 examples/cxx/tree/custom/mixed/people.xml create mode 100644 examples/cxx/tree/custom/mixed/people.xsd (limited to 'examples/cxx') diff --git a/examples/cxx/tree/custom/README b/examples/cxx/tree/custom/README index 482f5a9..b2a65b5 100644 --- a/examples/cxx/tree/custom/README +++ b/examples/cxx/tree/custom/README @@ -22,6 +22,9 @@ double XML Schema built-in type. It can be used as a guide on how to customize built-in XML Schema types that are mapped to fundamental C++ types. +mixed + Shows how to use type customization to parse and serialize mixed content. + taxonomy Shows how to map user-defined XML Schema types to custom C++ classes. This example presents the complex case where the customized types are diff --git a/examples/cxx/tree/custom/makefile b/examples/cxx/tree/custom/makefile index 961916e..fc851e3 100644 --- a/examples/cxx/tree/custom/makefile +++ b/examples/cxx/tree/custom/makefile @@ -5,8 +5,8 @@ include $(dir $(lastword $(MAKEFILE_LIST)))../../../../build/bootstrap.make -all_examples := comments contacts double taxonomy wildcard calendar -build_examples := comments contacts double taxonomy wildcard +all_examples := comments contacts double mixed taxonomy wildcard calendar +build_examples := comments contacts double mixed taxonomy wildcard ifeq ($(xsd_with_boost_date_time),y) build_examples += calendar diff --git a/examples/cxx/tree/custom/mixed/README b/examples/cxx/tree/custom/mixed/README new file mode 100644 index 0000000..7b56812 --- /dev/null +++ b/examples/cxx/tree/custom/mixed/README @@ -0,0 +1,50 @@ +This example shows how to use type customization to parse and serialize +mixed content. The example achieves this by customizing the type with +the mixed content model to include a DOM document that stores the data +as a raw XML representation. The customized type also provides its own +parsing constructor and serialization operator where the mixed content +is extracted from and inserted back to DOM, respectively. The use of +DOM for mixed content storage is one of the options. You may find other +data structures (e.g., a string) more suitable depending on your situation. + +For more information on the C++/Tree mapping customization see the C++/Tree +Mapping Customization Guide[1]. + +[1] http://wiki.codesynthesis.com/Tree/Customization_guide + +The example consists of the following files: + +people.xsd + XML Schema definition for a simple person record vocabulary. Each + record includes the bio element which represents arbitrary XHTML + fragments as mixed content. + +people.xml + Sample XML instance document. + +people.hxx +people.ixx +people.cxx + C++ types that represent the given vocabulary, a set of parsing + functions that convert XML instance documents to a tree-like in-memory + object model, and a set of serialization functions that convert the + object model back to XML. These are generated by XSD from people.xsd + with the --custom-type option in order to customize the bio type. + +people-custom.hxx + Header file which defines our own bio class by inheriting from the + generated bio_base. It is included at the end of people.hxx using + the --hxx-epilogue option. + +people-custom.cxx + Source file which contains the implementation of our bio class. + +driver.cxx + Driver for the example. It first calls one of the parsing functions + that constructs the object model from the input file. It then prints + the data to STDERR, including the bio information converted to text. + Finally, the driver serializes the object model back to XML. + +To run the example on the sample XML instance document simply execute: + +$ ./driver people.xml diff --git a/examples/cxx/tree/custom/mixed/driver.cxx b/examples/cxx/tree/custom/mixed/driver.cxx new file mode 100644 index 0000000..a6dd0a7 --- /dev/null +++ b/examples/cxx/tree/custom/mixed/driver.cxx @@ -0,0 +1,124 @@ +// file : examples/cxx/tree/custom/mixed/driver.cxx +// author : Boris Kolpackov +// copyright : not copyrighted - public domain + +#include // std::auto_ptr +#include + +#include +#include + +#include "people.hxx" + +// The following transcode() utility function is handy when working with +// Xerces. Include it after the generated header in order to get only char +// or wchar_t version depending on how you compiled your schemas. +// +#include + +using std::cerr; +using std::endl; +using namespace xercesc; + + +void +xhtml2txt (const DOMElement*); + +int +main (int argc, char* argv[]) +{ + if (argc != 2) + { + cerr << "usage: " << argv[0] << " people.xml" << endl; + return 1; + } + + int r (0); + + // The Xerces-C++ DOM document that will be used to store the XHTML + // fragments "out-live" the call to the parsing function. Therefore + // we need to initialize the Xerces-C++ runtime ourselves. + // + XMLPlatformUtils::Initialize (); + + try + { + using namespace people; + + // Parse. + // + std::auto_ptr d ( + directory_ (argv[1], xml_schema::flags::dont_initialize)); + + // Print what we've got. + // + const directory::person_sequence& s (d->person ()); + + for (directory::person_const_iterator i (s.begin ()); i != s.end (); ++i) + { + cerr << "First : " << i->first_name () << endl + << "Last : " << i->last_name () << endl + << "Gender : " << i->gender () << endl + << "Age : " << i->age () << endl; + + const bio& b (i->bio ()); + const DOMElement* xhtml (b.xhtml ()); + + if (xhtml != 0) + { + cerr << "Bio : " << endl; + xhtml2txt (xhtml); + } + + cerr << endl; + } + + // Serialize. + // + xml_schema::namespace_infomap map; + + map["ppl"].name = "http://www.codesynthesis.com/people"; + map["ppl"].schema = "people.xsd"; + + directory_ ( + std::cout, *d, map, "UTF-8", xml_schema::flags::dont_initialize); + } + catch (const xml_schema::exception& e) + { + cerr << e << endl; + r = 1; + } + + XMLPlatformUtils::Terminate (); + return r; +} + +// Primitive XHTML to text converter that just prints all the text +// nodes and ignores everything else. +// +void +xhtml2txt (const DOMElement* e) +{ + namespace xml = xsd::cxx::xml; + + for (const DOMNode* n (e->getFirstChild ()); + n != 0; + n = n->getNextSibling ()) + { + switch (n->getNodeType ()) + { + case DOMNode::TEXT_NODE: + { + cerr << xml::transcode (n->getTextContent ()); + break; + } + case DOMNode::ELEMENT_NODE: + { + xhtml2txt (static_cast (n)); + break; + } + default: + break; // Ignore all other nodes (e.g., comments, etc). + } + } +} diff --git a/examples/cxx/tree/custom/mixed/makefile b/examples/cxx/tree/custom/mixed/makefile new file mode 100644 index 0000000..67807a6 --- /dev/null +++ b/examples/cxx/tree/custom/mixed/makefile @@ -0,0 +1,106 @@ +# file : examples/cxx/tree/custom/mixed/makefile +# author : Boris Kolpackov +# copyright : Copyright (c) 2005-2009 Code Synthesis Tools CC +# license : GNU GPL v2 + exceptions; see accompanying LICENSE file + +include $(dir $(lastword $(MAKEFILE_LIST)))../../../../../build/bootstrap.make + +xsd := people.xsd +cxx := driver.cxx people-custom.cxx + +obj := $(addprefix $(out_base)/,$(cxx:.cxx=.o) $(xsd:.xsd=.o)) +dep := $(obj:.o=.o.d) + +driver := $(out_base)/driver +dist := $(out_base)/.dist +dist-win := $(out_base)/.dist-win +clean := $(out_base)/.clean + + +# Import. +# +$(call import,\ + $(scf_root)/import/libxerces-c/stub.make,\ + l: xerces_c.l,cpp-options: xerces_c.l.cpp-options) + + +# Build. +# +$(driver): $(obj) $(xerces_c.l) + +$(obj) $(dep): cpp_options := -I$(src_root)/libxsd +$(obj) $(dep): $(xerces_c.l.cpp-options) + +genf := $(xsd:.xsd=.hxx) $(xsd:.xsd=.ixx) $(xsd:.xsd=.cxx) +gen := $(addprefix $(out_base)/,$(genf)) + +$(gen): xsd := $(out_root)/xsd/xsd + +# We have to double-escape '#' because the message function +# (which is used in command scripts) expands things twice. +# +$(gen): xsd_options := \ +--generate-inline \ +--generate-serialization \ +--custom-type bio=/bio_base \ +--hxx-epilogue '\\\#include "people-custom.hxx"' + +$(gen): $(out_root)/xsd/xsd + +$(call include-dep,$(dep)) + +# Convenience alias for default target. +# +$(out_base)/: $(driver) + +# Dist. +# +dist-common := $(out_base)/.dist-common + +$(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)/people.xsd,$(dist_prefix)/$(path)/people.xsd) + $(call install-data,$(src_base)/people.xml,$(dist_prefix)/$(path)/people.xml) + $(call install-data,$(src_base)/people-custom.hxx,$(dist_prefix)/$(path)/people-custom.hxx) + $(call install-data,$(src_base)/people-custom.cxx,$(dist_prefix)/$(path)/people-custom.cxx) + +$(dist): $(dist-common) + $(call install-data,$(src_base)/README,$(dist_prefix)/$(path)/README) + +$(dist-win): $(dist-common) + $(call install-data,$(src_base)/README,$(dist_prefix)/$(path)/README.txt) + $(call message,,unix2dos $(dist_prefix)/$(path)/README.txt) + + +# Clean. +# +$(clean): $(driver).o.clean \ + $(addsuffix .cxx.clean,$(obj)) \ + $(addsuffix .cxx.clean,$(dep)) \ + $(addprefix $(out_base)/,$(xsd:.xsd=.cxx.xsd.clean)) + +# Generated .gitignore. +# +ifeq ($(out_base),$(src_base)) +$(gen): | $(out_base)/.gitignore +$(driver): | $(out_base)/.gitignore + +$(out_base)/.gitignore: files := driver $(genf) +$(clean): $(out_base)/.gitignore.clean + +$(call include,$(bld_root)/git/gitignore.make) +endif + +# How to. +# +$(call include,$(bld_root)/cxx/o-e.make) +$(call include,$(bld_root)/cxx/cxx-o.make) +$(call include,$(bld_root)/cxx/cxx-d.make) +$(call include,$(bld_root)/install.make) +$(call include,$(scf_root)/xsd/tree/xsd-cxx.make) + +# Dependencies. +# +$(call import,$(src_root)/xsd/makefile) diff --git a/examples/cxx/tree/custom/mixed/people-custom.cxx b/examples/cxx/tree/custom/mixed/people-custom.cxx new file mode 100644 index 0000000..af07e22 --- /dev/null +++ b/examples/cxx/tree/custom/mixed/people-custom.cxx @@ -0,0 +1,90 @@ +// file : examples/cxx/tree/custom/mixed/people-custom.cxx +// author : Boris Kolpackov +// copyright : not copyrighted - public domain + +#include + +// Include people.hxx instead of people-custom.hxx here. +// +#include "people.hxx" + +namespace people +{ + using namespace xercesc; + + const XMLCh ls[] = {chLatin_L, chLatin_S, chNull}; + + bio:: + bio () + : xhtml_ (0) + { + DOMImplementation* impl ( + DOMImplementationRegistry::getDOMImplementation (ls)); + + doc_.reset (impl->createDocument ()); + } + + bio:: + bio (const DOMElement& e, + xml_schema::flags f, + xml_schema::container* c) + : bio_base (e, f, c), xhtml_ (0) + { + DOMImplementation* impl ( + DOMImplementationRegistry::getDOMImplementation (ls)); + + doc_.reset (impl->createDocument ()); + + // Copy the xhtml element. Assume the first child element in + // e is always xhtml. + // + for (DOMNode* n (e.getFirstChild ()); n != 0; n = n->getNextSibling ()) + { + if (n->getNodeType () == DOMNode::ELEMENT_NODE) + { + xhtml_ = static_cast (doc_->importNode (n, true)); + break; + } + } + } + + bio:: + bio (const bio& d, + xml_schema::flags f, + xml_schema::container* c) + : bio_base (d, f, c), xhtml_ (0) + { + DOMImplementation* impl ( + DOMImplementationRegistry::getDOMImplementation (ls)); + + doc_.reset (impl->createDocument ()); + + xhtml_ = static_cast ( + doc_->importNode (const_cast (d.xhtml_), true)); + } + + bio* bio:: + _clone (xml_schema::flags f, xml_schema::container* c) const + { + return new bio (*this, f, c); + } + + void + operator<< (DOMElement& e, const bio& x) + { + // Allow our base to serialize first. + // + const bio_base& b (x); + e << b; + + // Copy the XHTML fragment if we have one. + // + const DOMElement* xhtml (x.xhtml ()); + + if (xhtml != 0) + { + DOMDocument* doc (e.getOwnerDocument ()); + e.appendChild (doc->importNode (const_cast (xhtml), true)); + } + } +} diff --git a/examples/cxx/tree/custom/mixed/people-custom.hxx b/examples/cxx/tree/custom/mixed/people-custom.hxx new file mode 100644 index 0000000..f05ba4a --- /dev/null +++ b/examples/cxx/tree/custom/mixed/people-custom.hxx @@ -0,0 +1,84 @@ +// file : examples/cxx/tree/custom/mixed/people-custom.hxx +// author : Boris Kolpackov +// copyright : not copyrighted - public domain + +// Do not include this file directly, use people.hxx instead. This +// file is included into generated people.hxx so we do not need to +// guard against multiple inclusions. +// + +#include +#include + +namespace people +{ + class bio: public bio_base + { + // Standard constructors. + // + public: + bio (); + + bio (const xercesc::DOMElement&, + xml_schema::flags = 0, + xml_schema::container* = 0); + + bio (const bio&, + xml_schema::flags = 0, + xml_schema::container* = 0); + + virtual bio* + _clone (xml_schema::flags = 0, + xml_schema::container* = 0) const; + + // XHTML bio as a DOM document. + // + public: + const xercesc::DOMElement* + xhtml () const + { + return xhtml_; + } + + xercesc::DOMElement* + xhtml () + { + return xhtml_; + } + + // The element should belong to the DOMDocument returned by + // the dom_document() functions. + // + void + xhtml (xercesc::DOMElement* e) + { + assert (e->getOwnerDocument () == doc_.get ()); + + if (xhtml_ != 0) + xhtml_->release (); + + xhtml_ = e; + } + + const xercesc::DOMDocument& + dom_document () const + { + return *doc_; + } + + xercesc::DOMDocument& + dom_document () + { + return *doc_; + } + + private: + xercesc::DOMElement* xhtml_; + xml_schema::dom::auto_ptr doc_; + }; + + // Serialization operator. + // + void + operator<< (xercesc::DOMElement&, const bio&); +} diff --git a/examples/cxx/tree/custom/mixed/people.xml b/examples/cxx/tree/custom/mixed/people.xml new file mode 100644 index 0000000..4e5c0ca --- /dev/null +++ b/examples/cxx/tree/custom/mixed/people.xml @@ -0,0 +1,39 @@ + + + + + + + + John + Doe + male + 32 + + +

Married to Jane Doe.

+
+
+
+ + + Jane + Doe + female + 28 + + +

Married to John Doe.

+
+
+
+ +
diff --git a/examples/cxx/tree/custom/mixed/people.xsd b/examples/cxx/tree/custom/mixed/people.xsd new file mode 100644 index 0000000..40c1fc1 --- /dev/null +++ b/examples/cxx/tree/custom/mixed/people.xsd @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/cxx/tree/mixed/README b/examples/cxx/tree/mixed/README index c6f8be7..9ab3309 100644 --- a/examples/cxx/tree/mixed/README +++ b/examples/cxx/tree/mixed/README @@ -1,6 +1,9 @@ This example shows how to access the underlying DOM nodes in the C++/Tree mapping in order to handle raw, "type-less content" such as mixed content models, anyType/anySimpleType, and any/anyAttribute. +For an alternative approach that employes type customization see +examples in the custom/ directory, in particular, custom/mixed and +custom/wildcard. In this example we use mixed content model to describe text with embedded links, e.g., -- cgit v1.1