XML Binding Development Guide : PART II Code Generation Examples : Chapter 3 Basic Purchase Order Example : The Example Code
The Example Code
To see how HydraExpress makes working with XML easy, take a look at the code used in the example.
Unmarshaling
The HydraExpress C++ generator creates a C++ API that allows you to unmarshal the XML shown in “Purpose” . Once the data is unmarshaled, you can analyze and manipulate the data using the type-safe C++ API. Finally, you can generate the XML that conforms to the above schema by taking the modified object and marshaling the object to XML.
Before you unmarshal the XML document, you must create an instance of the C++ type that corresponds with the root element of the XML document po.xml. In the purchase order example, this element is the purchaseOrder element. To create an instance of the purchaseOrder C++ type, instantiate the class that HydraExpress created for the type. In this case, the class is PurchaseOrder. The code sample below instantiates a PurchaseOrder object:
 
PurchaseOrder po;
To unmarshal an XML document, invoke the unmarshal() method on the object:
 
try {
std::ifstream istrm("po.xml");
std::stringstream buffer;
buffer << istrm.rdbuf();
std::string xmlContents(buffer.str());
po.unmarshal(xmlContents);
 
} catch (const rwsf::XmlParseException &e) {
std::cerr << "Parse error when unmashaling : " << e.what()
<< std::endl;
std::cerr << "Line number: " << e.getLineNumber() << std::endl;
std::cerr << "Column number: " << e.getColumnNumber()
<< std::endl;
return 1;
} catch (const rwsf::Exception &x) {
std::cerr << "Error : " << x.what() << std::endl;
return 1;
}
The code above opens a file called po.xml from the current working directory, reads the contents of that file into a std::ifstream object, and then asks the PurchaseOrder instance to unmarshal the XML. That is all it takes to use HydraExpress to parse the XML document.
Note that the code above shows the unmarshal method wrapped with a try-catch block. This allows the code to catch parsing and other errors and display an error message, as well as detail information about the particular error. Here is how to determine the kinds of exceptions you need to capture:
You should always catch at least rwsf::Exception. All exceptions thrown by HydraExpress Library classes are derived from this class. The example catches the more specific rwsf::XmlParseExceptionso it can display a more specific error message.
If you generate with the -stl option (the default), the implementation may need to catch std::exception. This example does not catch this exception because it does not use standard library classes.
If you generated with the -sourcepro option (not used in this example), you may need to catch RWxmsg exceptions, which can be thrown by SourcePro C++ classes.
Manipulating XML Content in C++
Manipulating the content of an XML tree in C++ is straightforward. You simply call the accessors and mutators on the generated object’s type-safe API. The following example illustrates how to change the shipping address of the unmarshaled XML instance to the billing address.
 
// Manipulate the object hierarchy by changing the "ship to"
// address to be the same as the "bill to" address
po.setShipTo(po.getBillTo());
Validating the Instance Document
To validate the simple type data in an instance document you can use the isValid() method. The basic isValid() method returns a bool value indicating whether all checks of simple type data in the document affirmed that the data was valid. To determine the exact nature of the errors in the event that isValid() returns false, you need to use the overload that passes in an rwsf::XmlValidator instance. The code below shows how you would check the errors for the PurchaseOrder instance.
 
rwsf::XmlValidator validator("temp");
if (!po.isValid(validator)) {
std::list<rwsf::XmlSchemaException> errs = validator.getErrors();
list::iterator iter = errs.begin();
while(iter != errs.end()) {
std::cout << (*iter).what() << std::endl;
}
}
Marshaling
To recreate XML content from the C++ object-oriented hierarchy, call the marshal() method on the root object purchaseOrder. This generated method correctly formats the XML per the constraints specified in the XML Schema and returns the XML as a string. The following example illustrates this step and prints the result to standard out.
 
// Invoke the marshal method on the object to create
// XML from the object and print the result to standard out
std::cout << po.marshal() << std::endl;
Working Without Top-Level Element Classes
By default, HydraExpress generates a class for each top-level element in the schema. This class is generated as a convenience. It has methods for marshaling and unmarshaling the element and has the advantage that the class knows the name of the element.
Sometimes, however, large and complex schemas have very many top-level elements, so creating a class for each one greatly increases the generated output. For this reason, HydraExpress offers the flag -notoplevelclasses to suppress classes for top-level elements. In this case, you marshal and unmarshal through the corresponding type class.
For example, specifying this flag when generating the basic example creates only the class PurchaseOrderType, and no class PurchaseOrder. To marshal a purchase order, you would use one of the marshal() methods of PurchaseOrderType. However, there is a problem. The PurchaseOrderType object does not know the name of the top-level element for which it is the type. To solve this problem, HydraExpress creates a class, PoStatics in this case, that holds only static variables for the element names of all top-level elements. For the basic example, this would be:
 
class PoStatics {
public:
static rwsf::XmlName PurchaseOrderElementName;
static rwsf::XmlName CommentElementName;
};
To marshal a purchase order, you would do the following:
 
PurchaseOrderType po;
std:cout << po.marshal(false, PoStatics:PurchaseOrderElementName) << std:endl;
The unmarshal() methods also take the element name as a parameter. This name is used to check that the document being unmarshaled uses the correct name for the element. For the basic example, a call to unmarshal() would look like this:
 
PurchaseOrderType po;
std::ifstream istrm("po.xml");
std::stringstream buffer;
buffer << istrm.rdbuf();
std::string xmlContents(buffer.str());
 
po.unmarshal(xmlContents,PoStatics::PurchaseOrderElementName);
 
When projects are generated with -notopLevelClasses, the generated documentation describes the Statics class and how it is used.