Tree/FAQ

From Code Synthesis Wiki

< TreeRevision as of 15:13, 17 June 2007; view current revision
←Older revision | Newer revision→
Jump to: navigation, search

Contents

General

What character encoding does the generated code use?

XSD has built-in support for two character types: char and wchar_t. You can select the character type with the --char-type command line option. The default character type is char. The character encoding depends on the character type used.

For the char character type the encoding is UTF-8. In XSD prior to version 2.3.1, the so-called "local code page" encoding was used via the Xerces-C++ XMLString::transcode functions. On some platforms (e.g., UNIX-like) you could set the local code page with the call to setlocale. On other platforms (e.g., Windows), the local code page is preset and cannot be changed. For backwards compatibility XSD allows you to use the local code page encoding by defining the XSD_USE_LCP preprocessor macro when compiling your source code.

For the wchar_t character type the encoding is automatically selected between UTF-16 and UTF-32/UCS-4 depending on the size of the wchar_t type. On some platforms (e.g., Windows with Visual C++ and AIX with IBM XL C++) wchar_t is 2 bytes long. For these platforms the encoding is UTF-16. On other platforms wchar_t is 4 bytes long and UTF-32/UCS-4 is used.

Parsing

How do I parse an XML instance to a Xerces-C++ DOM document?

While this question is not exactly about XSD or the C++/Tree mapping and it is covered in the Xerces-C++ Programming Guide, this step is a prerequisite to some more advanced techniques covered in this FAQ. Furthermore, the XSD runtime provides some untilities that make the code a little bit more palatable.

#include <istream>

#include <xercesc/dom/DOM.hpp>
#include <xercesc/framework/Wrapper4InputSource.hpp>

#include <xsd/cxx/xml/string.hxx>
#include <xsd/cxx/xml/dom/elements.hxx>
#include <xsd/cxx/xml/dom/bits/error-handler-proxy.hxx>
#include <xsd/cxx/xml/sax/std-input-source.hxx>

#include <xsd/cxx/tree/exceptions.hxx>
#include <xsd/cxx/tree/error-handler.hxx>

xsd::cxx::xml::dom::auto_ptr<xercesc::DOMDocument>
parse (std::istream& is)
{
  using namespace xercesc;
  namespace xml = xsd::cxx::xml;
  namespace tree = xsd::cxx::tree;

  const bool validate (true);
  const XMLCh ls_id [] = {chLatin_L, chLatin_S, chNull};

  // Get an implementation of the Load-Store (LS) interface.
  //
  DOMImplementation* impl (
    DOMImplementationRegistry::getDOMImplementation (ls_id));

  // Create a DOMBuilder.
  //
  xml::dom::auto_ptr<DOMBuilder> parser (
    impl->createDOMBuilder(DOMImplementationLS::MODE_SYNCHRONOUS, 0));

  // Discard comment nodes in the document.
  //
  parser->setFeature (XMLUni::fgDOMComments, false);

  // Enable datatype normalization.
  //
  parser->setFeature (XMLUni::fgDOMDatatypeNormalization, true);

  // Do not create EntityReference nodes in the DOM tree. No
  // EntityReference nodes will be created, only the nodes
  // corresponding to their fully expanded substitution text
  // will be created.
  //
  parser->setFeature (XMLUni::fgDOMEntities, false);

  // Perform Namespace processing.
  //
  parser->setFeature (XMLUni::fgDOMNamespaces, true);

  // Do not include ignorable whitespace in the DOM tree.
  //
  parser->setFeature (XMLUni::fgDOMWhitespaceInElementContent, false);

  // Enable/Disable validation.
  //
  parser->setFeature (XMLUni::fgDOMValidation, validate);
  parser->setFeature (XMLUni::fgXercesSchema, validate);
  parser->setFeature (XMLUni::fgXercesSchemaFullChecking, validate);

  // We will release the DOM document ourselves.
  //
  parser->setFeature (XMLUni::fgXercesUserAdoptsDOMDocument, true);

  // Set error handler.
  //
  tree::error_handler<char> eh;
  xml::dom::bits::error_handler_proxy<char> ehp (eh);
  parser->setErrorHandler (&ehp);

  // Prepare input stream.
  //
  xml::sax::std_input_source isrc (is);
  Wrapper4InputSource wrap (&isrc, false);

  xml::dom::auto_ptr<DOMDocument> doc (parser->parse (wrap));

  eh.throw_if_failed<tree::parsing<char> > ();

  return doc;
}

Below is a simple program that uses the above code.

#include <fstream>

int
main (int argc, char* argv[])
{
  using namespace xercesc;
  namespace xml = xsd::cxx::xml;

  XMLPlatformUtils::Initialize ();

  {
    std::ifstream ifs (argv[1]);
    xml::dom::auto_ptr<DOMDocument> doc (parse (ifs));
  }
 
  XMLPlatformUtils::Terminate ();
}

How do I handle XML data of an unknown type?

Here we assume that you need to handle XML instances that can be of several predefined types. There is no informtaion that distinguishes one instance from the other other than the root element name.

Suppose we have two root elements defined in our schema: foo and bar with types Foo and Bar, respectively. There are two ways to handle this situation. The first is quite straightforward but slow. It boils down to calling each parsing function in a sequence expecting all except one to fail. The slow part comes from the need to re-parse XML to DOM for each invocation. The following code outlines this approach:

while (true)
{
  try
  {
    std::auto_ptr<Foo> f (foo ("instance.xml")); // Try to parse as Foo.

    // Do something useful with f.

    break;
  }
  catch (xml_schema::unexpected_element const&)
  {
    // Try the next function.
  }

  try
  {
    std::auto_ptr<Bar> b (bar ("instance.xml")); // Try to parse as Bar.

    // Do something useful with b.

    break;
  }
  catch (xml_schema::unexpected_element const&)
  {
    // Try the next function.
  }

  // This instance is of some other type.
}

The second approach involves splitting the parsing process into two stages: XML to DOM and DOM to Tree. After the XML to DOM stage we peek at the root element and decide which parsing function to call:

#include <xercesc/dom/DOM.hpp>
#include <xsd/cxx/xml/string.hxx>

using namespace xercesc;

DOMDocument* dom = ... // Parse XML into DOM.
DOMElement* root = dom->getDocumentElement ();
std::string name (xsd::cxx::xml::transcode (root->getLocalName ()));

if (name == "foo")
{
  std::auto_ptr<Foo> f (foo (*dom)); // Parse dom to Foo.

  // Do something useful with f.
}
else if (name == "bar")
{
  std::auto_ptr<Bar> b (bar (*dom)); // Parse dom to Bar.

  // Do something useful with b.
}

For more information on parsing XML to DOM see How do I parse an XML instance to a Xerces-C++ DOM document?

Serialization

How do I create an empty Xerces-C++ DOM document?

While this question is not exactly about XSD or the C++/Tree mapping and it is covered in the Xerces-C++ Programming Guide, this step is a prerequisite to some more advanced techniques covered in this FAQ. Furthermore, the XSD runtime provides some untilities that make the code a little bit more palatable.

#include <xercesc/dom/DOM.hpp>

#include <xsd/cxx/xml/string.hxx>
#include <xsd/cxx/xml/dom/elements.hxx>

xsd::cxx::xml::dom::auto_ptr<xercesc::DOMDocument>
create (const std::string& root_element_name,
        const std::string& root_element_namespace = "",
        const std::string& root_element_namespace_prefix = "");

xsd::cxx::xml::dom::auto_ptr<xercesc::DOMDocument>
create (const std::string& name,
        const std::string& ns,
        const std::string& prefix)
{
  using namespace xercesc;
  namespace xml = xsd::cxx::xml;

  const XMLCh ls_id [] = {chLatin_L, chLatin_S, chNull};

  // Get an implementation of the Load-Store (LS) interface.
  //
  DOMImplementation* impl (
    DOMImplementationRegistry::getDOMImplementation (ls_id));

  xml::dom::auto_ptr<DOMDocument> doc (
    impl->createDocument (
      (ns.empty () ? 0 : xml::string (ns).c_str ()),
      xml::string ((prefix.empty () ? name : prefix + ':' + name)).c_str (),
      0));

  return doc;
}

The following code fragment shows how to use this function. It also shows how to establish additional namespace-prefix mappings and set the schemaLocation attribute:

int
main (int argc, char* argv[])
{
  using namespace xercesc;
  namespace xml = xsd::cxx::xml;

  XMLPlatformUtils::Initialize ();

  {
    xml::dom::auto_ptr<DOMDocument> doc (
      create ("example",
              "http://www.example.com/xmlns/example",
              "e"));

    DOMElement* root (doc->getDocumentElement ());

    root->setAttributeNS (
      xml::string ("http://www.w3.org/2000/xmlns/").c_str (),
      xml::string ("xmlns:xsi").c_str (),
      xml::string ("http://www.w3.org/2001/XMLSchema-instance").c_str ());

    root->setAttributeNS (
      xml::string ("http://www.w3.org/2001/XMLSchema-instance").c_str (),
      xml::string ("xsi:schemaLocation").c_str (),
      xml::string ("http://www.example.com/xmlns/example example.xsd").c_str ());
  }

  XMLPlatformUtils::Terminate ();
}

The call to create above creates a DOM document with the example element as its root. The example element is in the http://www.example.com/xmlns/example namespace to which we assigned the e namespace prefix.

How do I serialize a Xerces-C++ DOM document to XML?

While this question is not exactly about XSD or the C++/Tree mapping and it is covered in the Xerces-C++ Programming Guide, this step is a prerequisite to some more advanced techniques covered in this FAQ. Furthermore, the XSD runtime provides some untilities that make the code a little bit more palatable.

#include <ostream>

#include <xercesc/dom/DOM.hpp>
#include <xercesc/framework/Wrapper4InputSource.hpp>

#include <xsd/cxx/xml/string.hxx>
#include <xsd/cxx/xml/dom/elements.hxx>
#include <xsd/cxx/xml/dom/serialization.hxx>
#include <xsd/cxx/xml/dom/bits/error-handler-proxy.hxx>

#include <xsd/cxx/tree/exceptions.hxx>
#include <xsd/cxx/tree/error-handler.hxx>

void
serialize (std::ostream& os,
           const xercesc::DOMDocument& doc,
           const std::string& encoding = "UTF-8")
{
  using namespace xercesc;
  namespace xml = xsd::cxx::xml;
  namespace tree = xsd::cxx::tree;

  const XMLCh ls_id [] = {chLatin_L, chLatin_S, chNull};

  // Get an implementation of the Load-Store (LS) interface.
  //
  DOMImplementation* impl (
    DOMImplementationRegistry::getDOMImplementation (ls_id));

  // Create a DOMWriter.
  //
  xml::dom::auto_ptr<DOMWriter> writer (impl->createDOMWriter ());

  // Set error handler.
  //
  tree::error_handler<char> eh;
  xml::dom::bits::error_handler_proxy<char> ehp (eh);
  writer->setErrorHandler (&ehp);

  // Set encoding.
  //
  writer->setEncoding(xml::string (encoding).c_str ());

  // Set some generally nice features.
  //
  writer->setFeature (XMLUni::fgDOMWRTDiscardDefaultContent, true);
  writer->setFeature (XMLUni::fgDOMWRTFormatPrettyPrint, true);

  // Adapt ostream to format target and serialize.
  //
  xml::dom::ostream_format_target oft (os);

  writer->writeNode (&oft, doc);

  eh.throw_if_failed<tree::parsing<char> > ();
}

This function can be used like this:

#include <fstream>

int
main (int argc, char* argv[])
{
  using namespace xercesc;
  namespace xml = xsd::cxx::xml;

  XMLPlatformUtils::Initialize ();

  {
    DOMDocument& doc = ...

    std::ofstream ofs (argv[1]);

    serialize (ofs, *doc);
  }

  XMLPlatformUtils::Terminate ();
}

See also

Personal tools