# Introduction

While some of us may agree that XML is a human-readable format, most of us would prefer not to prove it is human-writable. This guide shows how we can rather make our applications do the writing for us.

A typical application that deals with XML instance documents usually performs the following three steps: it first reads (parses) an XML instance document to an in-memory representation, it then performs some useful computations on that representation which may involve modification of the representation, and finally it writes (serializes) the modified in-memory representation to XML.

This document covers various aspects of serializing an xsd-generated tree-like in-memory representation back to DOM or XML. In this regard, serialization is complimentary to the reverse process of creating an in-memory representation from DOM or XML.

You may want to refer to the library example in the `examples/cxx/tree/library` directory for the actual code fragments.

For each globally-defined element in the form:

    <xsd:element name="name" type="type"/>

xsd generates six overloaded serialization functions in the form:

    // Serialize to std::ostream.
    //
    void
    name (std::ostream&,
          type const&,
          xml_schema::namespace_infomap const&,
          string const& encoding = "UTF-8");

    void
    name (std::ostream&,
          type const&,
          xml_schema::namespace_infomap const&,
          xercesc::DOMErrorHandler&,
          string const& encoding = "UTF-8");

    // Serialize to XMLFormatTarget.
    //
    void
    name (xercesc::XMLFormatTarget&,
          type const&,
          xml_schema::namespace_infomap const&,
          string const& encoding = "UTF-8");

    void
    name (xercesc::XMLFormatTarget&,
          type const&,
          xml_schema::namespace_infomap const&,
          xercesc::DOMErrorHandler&,
          string const& encoding = "UTF-8");

    // Serialize to DOM.
    //
    xml::auto_ptr<xercesc::DOMDocument>
    name (type const&, xml_schema::namespace_infomap const&);

    void
    name (xercesc::DOMDocument&, type const&);

You can choose between writing XML to `std::ostream` or `xercesc::XMLFormatTarget` and creating a DOM instance in the form of `xercesc::DOMDocument`. Serialization to `ostream` or `XMLFormatTarget` requires a considerably less amount of work while serialization to DOM provides for greater flexibility.

Some serialization functions expect you to initialize the Xerces-C++ runtime while others initialize and terminate it as part of their work. The general rule is as follows: if a function has any arguments or return a value that is an instance of a Xerces-C++ type, then this function expects you to initialize the Xerces-C++ runtime. Otherwise, the function initializes and terminates the runtime for you. Note that it is legal to have nested calls to the Xerces-C++ initialize and terminate functions as long as the calls are balanced.

# Namespace infomap, character encoding and error handling

When a document being serialized uses XML namespaces, prefix-namespace associations need to be established. Also, if you would like the resulting instance document to contain the `schemaLocation` or `noNamespaceSchemaLocation` attributes, you will need to provide namespace-schema associations. That's what `xml_schema::namespace_infomap` is for:

    namespace xml_schema
    {
      struct namespace_info
      {
        namespace_info ();
        namespace_info (string const& name, string const& schema);

        string name;
        string schema;
      };

      // Map of namespace prefix to namespace_info.
      //
      struct namespace_infomap: public std::map<string, namespace_info>
      {
      };
    }

Consider the following associations as an example:

    xml_schema::namespace_infomap map;

    map["lib"].name = "http://www.codesynthesis.com/lib";
    map["lib"].schema = "library.xsd";

This map, if passed to one of the serialization functions, will result in the following XML fragment:

    <?xml version="1.0" ?>
    <lib:library xmlns:lib="http://www.codesynthesis.com/lib"
                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                 xsi:schemaLocation="http://www.codesynthesis.com/lib library.xsd">

As you can see, the serialization function automatically added namespace mapping for the `xsi` prefix. You can change this by providing your own prefix:

    xml_schema::namespace_infomap map;

    map["xsn"].name = "http://www.w3.org/2001/XMLSchema-instance";

    map["lib"].name = "http://www.codesynthesis.com/lib";
    map["lib"].schema = "library.xsd";

This will result in the following XML fragment:

    <?xml version="1.0" ?>
    <lib:library xmlns:lib="http://www.codesynthesis.com/lib"
                 xmlns:xsn="http://www.w3.org/2001/XMLSchema-instance"
                 xsn:schemaLocation="http://www.codesynthesis.com/lib library.xsd">

Another bit of information that you can pass to the serialization functions is the character encoding method that you would like to use. Common values for this argument are `"USASCII"`, `"ISO8859-1"`, `"UTF-8"`, `"UTF-16BE"`, `"UTF-16LE"`, `"UCS-4BE"`, and `"UCS-4LE"`. The default encoding is `"UTF-8"`. For more information on encoding methods see the Character Encoding article from Wikipedia.

The final piece of information that you need to know is error handling strategies employed by the serialization functions. Currently there are two complimentary ways of reporting serialization failures: by throwing exceptions and via `xercesc::DOMErrorHandler`.

For each serialization function that has a `DOMErrorHandler` reference as an argument, there is an overloaded function that does not have this argument. Such a function uses a default implementation of the `DOMErrorHandler` interface which prints all error messages to `std::cerr` or `std::wcerr`, depending on the character type you used when you translated your schemas.

The following exceptions can be thrown during the serialization process (defined in `xsd/cxx/tree/exceptions.hxx`):

    namespace xml_schema
    {
      struct unexpected_element: virtual exception
      {
        virtual char const*
        what () const throw ();

        // Encountered element.
        //
        string const&
        element () const;

        // Space-separated list of names of expected elements.
        //
        string const&
        expected () const
      };
    }

`xml_schema::unexpected_element` exception is thrown if the root element name of the provided DOM instance does not match with the name of the element this serialization function is for.

    namespace xml_schema
    {
      struct serialization: virtual exception
      {
        virtual char const*
        what () const throw ();
      };
    }

`xml_schema::serialization` exception is thrown if an error has been reported via the `DOMErrorHandler` interface.

    namespace xml_schema
    {
      struct mapping: virtual exception
      {
        virtual char const*
        what () const throw ();

        // Namespace name for which a prefix-namespace mapping hasn't
        // been provided.
        //
        string const&
        name () const;
      };
    }

`xml_schema::mapping` exception is thrown if a namespace name is encountered for which a prefix-namespace mapping hasn't been provided.

    namespace xml_schema
    {
      struct xsi_already_in_use: virtual exception
      {
        virtual char const*
        what () const throw ();
      };
    }

`xml_schema::xsi_already_in_use` exception is thrown if the `xsi` prefix is already in use and no user-defined prefix-namespace mapping has been provided for the `"http://www.w3.org/2001/XMLSchema-instance"` namespace.

# Serializing to `std::ostream`

Serialization to `std::ostream` is simply a matter of providing an in-memory representation, an output stream and a namespace infomap. The following code snippet is from the library example:

    std::auto_ptr<library> l = ...

    // Prepare namespace mapping and schema location information.
    //
    xml_schema::namespace_infomap map;

    map["lib"].name = "http://www.codesynthesis.com/lib";
    map["lib"].schema = "library.xsd";

    // Write it out.
    //
    library (std::cout, *l, map);

Note that the output stream is treated as a binary stream. This becomes important when you use a character encoding that is wider than 8-bit `char`, for example UTF-16 or UCS-4. For example, things will most likely break if you try to serialize to `std::ostringstream` with UTF-16 or UCS-4 as an encoding. This is due to the special value, `'\0'`, that will most likely occur as part of such serialization and it won't have the special meaning assumed by `std::ostringstream`.

# Serializing to `xercesc::XMLFormatTarget`

Serialization to `xercesc::XMLFormatTarget` is equally easy with the following example illustrating the idea:

    std::auto_ptr<library> l = ...

    // Prepare namespace mapping and schema location information.
    //
    xml_schema::namespace_infomap map;

    map["lib"].name = "http://www.codesynthesis.com/lib";
    map["lib"].schema = "library.xsd";

    using namespace xercesc;

    XMLPlatformUtils::Initialize ();

    {
      // Choose a target.
      //
      std::auto_ptr<XMLFormatTarget> ft;

      if (argc != 2)
      {
        ft = std::auto_ptr<XMLFormatTarget> (new StdOutFormatTarget ());
      }
      else
      {
        ft = std::auto_ptr<XMLFormatTarget> (new LocalFileFormatTarget (argv[1]));
      }

      // Write it out.
      //
      library (*ft, *l, map);
    }

    XMLPlatformUtils::Terminate ();

Note that we had to initialize the Xerces-C++ runtime before we could call this serialization function.

# Serializing to DOM

There are two overloaded functions that implement serialization to a DOM instance. The one creates a DOM instance for you and the other serializes to an existing DOM instance. While serializing to a new DOM instance is similar to serializing to `std::ostream` or `xercesc::XMLFormatTarget`, serializing to an existing DOM instance requires quite a bit of work from your side. You will need to set all the namespace mapping attributes as well as the `schemaLocation` and/or `noNamespaceSchemaLocation` attributes. The following listing will give you an idea about what needs to be done:

    std::auto_ptr<library> l = ...

    using namespace xercesc;
    using namespace xsd::cxx;

    XMLPlatformUtils::Initialize ();

    {

      DOMImplementation* impl (
        DOMImplementationRegistry::getDOMImplementation (
          xml::string ("LS").c_str ()));

      // Create a DOM instance.
      //
      xml::auto_ptr<DOMDocument> doc (
        impl->createDocument (
          //
          // Root element namespace.
          //
          xml::string ("http://www.codesynthesis.com/lib").c_str (),
          //
          // Root element name. Note that the namespace prefix is automatically
          // associated with the root element namespace above.
          //
          xml::string ("lib:library").c_str (),
          //
          // Document type object.
          //
          0));

      // Set namespace mapping and schema location attributes.
      //
      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.codesynthesis.com/lib library.xsd").c_str ());

      // Serialize to DOM.
      //
      library (*doc, *l);

      // Get an instance of DOMWriter.
      //
      xml::auto_ptr<DOMWriter> writer (impl->createDOMWriter ());

      // Plug in an error handler.
      //
      xml::error_handler<char> eh;
      writer->setErrorHandler (&eh);

      // Set some nice features if the writer supports them.
      //
      if (writer->canSetFeature (XMLUni::fgDOMWRTDiscardDefaultContent, true))
        writer->setFeature (XMLUni::fgDOMWRTDiscardDefaultContent, true);

      if (writer->canSetFeature (XMLUni::fgDOMWRTFormatPrettyPrint, true))
        writer->setFeature (XMLUni::fgDOMWRTFormatPrettyPrint, true);

      // Create a format target that will receive the resulting
      // XML stream from the writer.
      //
      std::auto_ptr<XMLFormatTarget> ft;

      if (argc != 2)
      {
        ft = std::auto_ptr<XMLFormatTarget> (new StdOutFormatTarget ());
      }
      else
      {
        ft = std::auto_ptr<XMLFormatTarget> (new LocalFileFormatTarget (argv[1]));
      }

      // Write to the format target.
      //
      writer->writeNode (ft.get (), *doc);
    }

    XMLPlatformUtils::Terminate ();