Quick Links:

bal | bbl | bdl | bsl

Namespaces | Defines

Component balxml_decoder
[Package balxml]

Provide a generic translation from XML into C++ objects. More...

Namespaces

namespace  balxml

Defines

#define BALXML_DECODER_LOG_ERROR(reporter)
#define BALXML_DECODER_LOG_WARNING(reporter)
#define BALXML_DECODER_LOG_END

Detailed Description

Outline
Purpose:
Provide a generic translation from XML into C++ objects.
Classes:
balxml::Decoder an XML decoder
See also:
Component balxml_decoderoptions, Component balxml_encoder, Component balber_berdecoder
Description:
This component provides a class balxml::Decoder for decoding value-semantic objects in XML format. The decode methods are function templates that will decode any object that meets the requirements of a sequence or choice object as defined in the bdlat_sequencefunctions and bdlat_choicefunctions components. These generic frameworks provide a common compile-time interface for manipulating struct-like and union-like objects.
There are two usage models for using balxml::Decoder. The common case, when the type of object being decoded is known in advance, involves calling one of a set of decode method templates that decode a specified value-semantic object from a specified stream or other input source. The caller may specify the input for decode as a file, an bsl::istream, an bsl::streambuf, or a memory buffer.
A less common but more flexible usage model involves calling the open to open the XML document from the specified input, then calling decode to decode to an object without specifying the input source, and finally calling close to close the input source. The open method positions the internal reader to the root element node, so the caller can examine the root element, decide what type of object is contained in the input stream/source, and construct an object of the needed type before calling decode to read from the already open input source. Thus the input data is not constrained to a single root element type.
Although the XML format is very useful for debugging and for conforming to external data-interchange specifications, it is relatively expensive to encode and decode and relatively bulky to transmit. It is more efficient to use a binary encoding (such as BER) if the encoding format is under your control. (See balber_berdecoder.)
Usage:
This section illustrates intended use of this component.
Example 1: Generating Code from a Schema:
Suppose we have the following XML schema inside a file called employee.xsd:
  <?xml version='1.0' encoding='UTF-8'?>
  <xs:schema xmlns:xs='http://www.w3.org/2001/XMLSchema'
             xmlns:test='http://bloomberg.com/schemas/test'
             targetNamespace='http://bloomberg.com/schemas/test'
             elementFormDefault='unqualified'>

      <xs:complexType name='Address'>
          <xs:sequence>
              <xs:element name='street' type='xs:string'/>
              <xs:element name='city'   type='xs:string'/>
              <xs:element name='state'  type='xs:string'/>
          </xs:sequence>
      </xs:complexType>

      <xs:complexType name='Employee'>
          <xs:sequence>
              <xs:element name='name'        type='xs:string'/>
              <xs:element name='homeAddress' type='test:Address'/>
              <xs:element name='age'         type='xs:int'/>
          </xs:sequence>
      </xs:complexType>

      <xs:element name='Address' type='test:Address'/>
      <xs:element name='Employee' type='test:Employee'/>

  </xs:schema>
Using the bas_codegen.pl tool, we can generate C++ classes for this schema:
  $ bas_codegen.pl -m msg -p test -E xsdfile.xsd
This tool will generate the header and implementation files for the test_address and test_employee components in the current directory.
The following function decodes an XML string into a test::Employee object and verifies the results:
  #include <test_employee.h>
  #include <balxml_decoder.h>
  #include <balxml_decoderoptions.h>
  #include <balxml_errorinfo.h>
  #include <balxml_minireader.h>
  #include <bsl_sstream.h>

  using namespace BloombergLP;

  int main()
  {
      const char INPUT[] = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
                           "<Employee>\n"
                           "    <name>Bob</name>\n"
                           "    <homeAddress>\n"
                           "        <street>Some Street</street>\n"
                           "        <city>Some City</city>\n"
                           "        <state>Some State</state>\n"
                           "    </homeAddress>\n"
                           "    <age>21</age>\n"
                           "</Employee>\n";

      bsl::stringstream ss(INPUT);

      test::Employee bob;

      balxml::DecoderOptions options;
      balxml::MiniReader     reader;
      balxml::ErrorInfo      errInfo;

      balxml::Decoder decoder(&options, &reader, &errInfo);

      decoder.decode(ss, &bob);

      assert(ss);
      assert("Bob"         == bob.name());
      assert("Some Street" == bob.homeAddress().street());
      assert("Some City"   == bob.homeAddress().city());
      assert("Some State"  == bob.homeAddress().state());
      assert(21            == bob.age());

      return 0;
  }
Example 2: Error and Warning Streams:
The following snippets of code illustrate how to pass an error stream and warning stream to the decode function. We will use the same test_employee component from the previous usage example. Note that the input XML string contains an error. (The homeAddress object has an element called country, which does not exist in the schema.):
  #include <test_employee.h>
  #include <balxml_decoder.h>
  #include <balxml_decoderoptions.h>
  #include <balxml_errorinfo.h>
  #include <balxml_minireader.h>
  #include <bsl_sstream.h>

  using namespace BloombergLP;

  int main()
  {
      const char INPUT[] = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
                           "<Employee>\n"
                           "    <name>Bob</name>\n"
                           "    <homeAddress>\n"
                           "        <street>Some Street</street>\n"
                           "        <city>Some City</city>\n"
                           "        <state>Some State</state>\n"
                           "        <country>Some Country</country>\n"
                           "    </homeAddress>\n"
                           "    <age>21</age>\n"
                           "</Employee>\n";

      bsl::stringstream ss(INPUT);

      test::Employee bob;

      balxml::DecoderOptions options;
      balxml::MiniReader     reader;
      balxml::ErrorInfo      errInfo;

      options.setSkipUnknownElements(false);
      balxml::Decoder decoder(&options, &reader, &errInfo,
                              &bsl::cerr, &bsl::cerr);
      decoder.decode(ss, &bob);

      assert(!ss);

      return 0;
  }
Note that the input stream is invalidated to indicate that an error occurred. Also note that the following error message will be printed on bsl::cerr:
  employee.xml:8.18: Error: Unable to decode sub-element 'country'.\n"
  employee.xml:8.18: Error: Unable to decode sub-element 'homeAddress'.\n";
The following snippets of code illustrate how to open decoder and read the first node before calling decode:
  int main()
  {
      const char INPUT[] =
          "<?xml version='1.0' encoding='UTF-8' ?>\n"
          "<Employee xmlns='http://www.bde.com/bdem_test'>\n"
          "    <name>Bob</name>\n"
          "    <homeAddress>\n"
          "        <street>Some Street</street>\n"
          "        <state>Some State</state>\n"
          "        <city>Some City</city>\n"
          "        <country>Some Country</country>\n"
          "    </homeAddress>\n"
          "    <age>21</age>\n"
          "</Employee>\n";

      balxml::MiniReader     reader;
      balxml::ErrorInfo      errInfo;
      balxml::DecoderOptions options;

      balxml::Decoder decoder(&options, &reader, &errInfo,
                              &bsl::cerr, &bsl::cerr);
Now we open the document, but we don't begin decoding yet:
      int rc = decoder.open(INPUT, sizeof(INPUT) - 1);
      assert(0 == rc);
Depending on the value of the first node, we can now determine whether the document is an Address object or an Employee object, and construct the target object accordingly:
      if (0 == bsl::strcmp(reader.nodeLocalName(), "Address")) {
          test::Address addr;
          rc = decoder.decode(&addr);
          bsl::cout << addr;
      }
      else {
          test::Employee bob;
          rc = decoder.decode(&bob);
          bsl::cout << bob;
      }

      assert(0 == rc);
When decoding is complete, we must close the decoder object:
      decoder.close();
      return 0;
  }

Define Documentation

#define BALXML_DECODER_LOG_ERROR (   reporter  ) 
Value:
do {                                                       \
        balxml::Decoder_ErrorLogger                             \
            logger(balxml::ErrorInfo::e_ERROR, reporter);  \
        logger.stream()
#define BALXML_DECODER_LOG_WARNING (   reporter  ) 
Value:
do {                                                       \
        balxml::Decoder_ErrorLogger                             \
           logger(balxml::ErrorInfo::e_WARNING, reporter); \
        logger.stream()
#define BALXML_DECODER_LOG_END
Value:
bsl::flush;                \
    } while (false)