If the LMX code generator seems like the tool you need (see What is LMX? to help you with that), the first thing to do is download and install a copy. You can evaluate a limited functionality version of LMX without a license. Having decided that LMX is what you need, you need to acquire a license file and store it on your PC. You will then be able to generate code by following the Quick Start instructions. Later you may wish to adapt LMX to better meet your requirements. Information on how to do this is covered in section 3 - In More Depth.
2.1.3 - Code Generation Using the Linux Command Line Version
2.3 - Unmarshaling (simple form)
2.4 - Marshaling (simple form)
2.5 - Interfacing with the Generated Classes
2.2 - C++ Compiling and Building
2.1.1 - Code Generation Using the Windows Interface
2.1.2 - Code Generation Using the Windows (DOS) Command Line Version
2.1.3 - Code Generation Using the Linux Command Line Version
2.2 - C++ Compiling and Building
2.3 - Unmarshaling (simple form)
2.4 - Marshaling (simple form)
2.5 - Interfacing with the Generated Classes
2.5.1 - Singular Empty Type C++ Interface
2.5.2 - Optional Empty Type C++ Interface
2.5.3 - Singular Simple Type C++ Interface
2.5.4 - Optional Simple Type C++ Interface
2.5.5 - Multiple Simple Type and List Simple Type C++ Interface
2.5.6 - Singular Complex Type C++ Interface
2.5.6.1 - Singular Complex Type Usage Examples
2.5.7 - Optional Complex Type C++ Interface
2.5.7.1 - Optional Complex Type Usage Examples
2.5.8 - Multiple Complex Type C++ Interface
2.5.8.1 - Multiple Complex Type Usage Examples
2.5.9 - Multiple xs:anyAttribute C++ Interface
2.5.10 - Singular xs:any C++ Interface
2.5.11 - Optional xs:any C++ Interface
2.5.12 - Multiple xs:any C++ Interface
2.5.13 - Additional Polymorphic Methods
2.5.13.1 - Finding the Identity of a Polymorphic Class
2.5.13.2 - Polymorphic Cloning
2.7 - The Abbreviated Type Notation
2.7.1 - Abbreviated Type Notation Examples
3.1 - Unmarshaling (advanced forms)
3.1.1 - Unmarshaling with Additional Control when Reading XML Input
3.2 - Marshaling (advanced forms)
3.3 - Selecting the Input File Type (XSD, WSDL, DTD)
3.4 - The Command-line and Configuration File Format
3.6.1 - Handling SOAP Messages
3.6.2 - HTTP Operations for Web Services
3.6.2.1 - SOAP Message to Message Operations
3.6.2.2 - SOAP Object to Object Operations
3.6.2.3 - Simple SOAP Operations
3.7 - Naming of Methods and Variables
3.8 - Adapting LMX to Your Environment
3.8.1 - Modifying Schema Type to C++ Type Mapping
3.8.1.1 - Input/Output Converters
3.9 - Augmenting Generated Classes With Your Own Code
3.9.1 - Augmenting Generated Classes with Snippet Event Handlers
3.11 - Debugging and Handling Errors
3.11.2 - Changing the Error Handling Behavior
3.11.3 - Conditional Error Handling
3.11.4 - Collecting Debug Error Information when using Convenience Methods
3.12 - Adding Extra Namespace Information
3.13 - Adding Schema Location Information
3.14 - Adding an XMLDecl to the XML output
3.15 - Multiple Schemas that Share Common Schemas
3.16 - Pattern Facet Handling Customization
3.17 - XML Output Format Customization
3.18 - mustUnderstand / Comprehension Required
3.19 - Finding an XML Instance's Namespace
3.20 - Setting a Decimal Value With a Float
It is primarily intended to be used in data oriented applications, but can also be used in document oriented applications.
The code generator runs on a Windows or Linux PC, but the output is generated in C++ source code that is portable to most platforms that support C++ templates and basic STL containers. (Contact us if the generated code does not run on your platform.)
The code generator comes with its own lightweight XML pull parser and uses C++ ostreams for output. Thus it is a complete XML solution that does not require additional components in order to interact with XML data.
1.2 - Benefits of Using LMX
1.3 - Download & Installation
Download and installation of the LMX code generator is the simple task of downloading the installation program via http://www.codalogic.com/lmx/, storing it on your PC and then running it.
You can operate the code generator with limited functionality for evaluation purposes without obtaining a license.
Additional information about the Evaluation Support Suite can be found in the LMX folder on the Windows Start menu.
If you have your own schema and example XML instances, LMX can generate a simple test framework. This consists of an additionally generated C++ file that exercises the main generated code. The test framework code unmarshals an XML instance from a file, does a deep copy to a new set of objects, and then marshals the result out as XML to a new file. You can then visually compare the original and generated files. To use this feature, check the "Generate Test Framework File" box on the "Options" tab of WinLMX (or specify the
If you would like to see examples of how to use the generated code, then the generated copy constructors (of the form
A worked example is also available at http://www.codalogic.com/lmx/examples.php to further help with the evaluation process.
We have not included a description of this example, other than the comments that appear in the code. (Full documentation on how to interface with LMX generated code appears in 2.5 - Interfacing with the Generated Classes.) You should find however that this example is straightforward and intuitive to understand, demonstrating that it is easy to learn how to use LMX. This makes your code faster to develop and easier to understand.
Example code similar to this can be found in the
The schema:
To fully use the product a license file must be acquired. This will be e-mailed to you when you purchase a license. The license file must be saved in the same directory as the LMX code generator executable.
To purchase a license, follow the links and instructions for purchasing at: http://www.codalogic.com/lmx/
3.8
3.7.1
3.7
3.6
3.5
3.4
3.3
3.2.5
3.2.4
3.2.3
3.2
3.1
3.0
3.0 Beta
2.11
2.10
2.9
2.8
2.7
2.6
2.5
2.4
2.3
The generated code is cross-platform.
When you start WinLMX, you will see a window displayed that contains a tabbed dialog. You can specify what you want WinLMX to do by selecting the various tabs, and completing the dialog items that are displayed.
The main aspect of configuration is selecting the files to be compiled, and the names of the output files. This is configured using the left-most tab labelled 'Schema Files'. Enter the base schema file in the top-most edit box, and the root of the output files (e.g. 'File' to generate 'File.h' and 'File.cpp') in the bottom-most edit box. The files that the schema imports should be entered into the middle list box by pressing the associated 'Add...' button.
LMX can parse both W3C Schema (XSD) and XML (external) DTD files. LMX can also locate and parse W3C Schema definitions embedded in WSDL files. The parsing that LMX performs depends on the file extension of the base schema file. See 3.3 - Selecting the Input File Type (XSD, WSDL, DTD) for more information.
When you have completed configuring WinLMX you can save the configuration using the 'File' menu.
To generate code for a configuration, press the 'Compile...' button at the bottom of the window. This will automatically select the 'Compilation Results' tab and display the results of the compilation.
Note that when a configuration file is loaded into WinLMX, the current working directory is set to the path of the configuration file. This allows you to specify relative file names for the schema and output files.
In addition to using the 'File' menu to open configuration files, WinLMX also supports drag and drop.
The configuration files created by WinLMX can be used by the command-line version of LMX. This allows you to develop configuration files using the windows version, and use that configuration in your build strategy with the command-line version. (If the command-line version of LMX is called with a single argument that ends in '.lmxprj' the tool assumes that it is a configuration file. Alternatively, the '-f' command-line option can be used to specify a configuration file.)
If WinLMX's default project settings are not suitable for your development environment, configure the settings as you would like them and then select the 'File->Save Default New Project' menu item. These settings will then be used when a new project is started, or a '.xsd' file is dragged over WinLMX.
The
If the primary schema definition references other schemas, these additional schema files are specified on the command line by including the + character, a space, and then the name of the file. Any number of additional schema files can be specified in this way.
If the additional schema files contain global elements that you would like to treat as possible XML instance document elements, then instead of using the
The last argument specifies the names of the C++ files into which the generated code is to be placed. The LMX code generator will append .cpp to the name specified for the C++ source file, and append .h to the name for the header file.
For example, the command line:
If my_xsd.xsd refers to other schemas, an example command line would be:
If two schemas refer to a common schema, one way to generate code is to use a command line similar to:
When using a licensed Linux version it is necessary to set and export the LMXDIR environment variable to the directory containing the
To integrate the generated code with your code, you will need to compile the generated code using your C++ compiler. As part of this, you will need to save the files
If you are using the LMX code generator in evaluation mode, or have not purchased the supporting software source code, you will have to link the libraries for the supporting software into your code (for example
If you are using the LMX code generator in licensed mode, and want to build the code for a platform other than Windows and Linux x86, you will need to compile the files
In addition to the header files generated by LMX, the LMX supporting software contains further header files. These are:
Which files you include depends on whether you do marshaling and unmarshaling operations in the file in question or not.
If you interface to the LMX generated classes, but do not do marshaling or unmarshaling, then you only need to include the .h file generated by LMX. This will automatically include the LMX header files
For the class generated at the top of the class hierarchy (e.g. c_root) LMX generates convenience functions to help the marshaling and unmarshaling process. These functions are not generated for other classes in order to reduce generated code size. This section first presents the method for unmarshaling using these convenience methods. Later in the section an alternative method of unmarshaling is presented, which can be used with other classes. Additionally, advanced unmarshaling techniques are presented in 3.1 - Unmarshaling (advanced forms).
To read XML from an in-memory buffer, code similar to the following can be used:
If it is not convenient to unmarshal at the time the object is constructed (for example, if the object is a static global store of the application's user preferences) then the following methods can be used.
To read XML from an in-memory buffer, code similar to the following can be used:
For the class generated at the top of the class hierarchy (e.g. c_root) LMX generates convenience functions to help the marshaling and unmarshaling process. These functions are not generated for other classes in order to reduce generated code size. This section first presents the method for marshaling using these convenience methods. Later in the section an alternative method of marshaling is presented, which can be used with other classes. Additionally, advanced marshaling techniques are presented in 3.2 - Marshaling (advanced forms).
A typical section of marshaling code for writing the XML to a string looks as follows:
The equivalent code for writing to a file is as follows:
and the code for marshaling to a file is:
An interface to an item depends in one respect on whether an item is:
A further consideration for optional items is whether the item is in an xs:choice construct.
There are also special functions to interface with enumerated values, and additional functions to interface to complex types that are polymorphic.
The following sections describe the interface for each of these combinations. Throughout the discussion, the string
An element of type
If the item is not in an xs:choice, the following methods are generated:
If the complex type is an xs:all, the following methods are generated:
A nillable type will always have a class generated for it, even if it is a simple type. (In effect a nillable simple type is treated as complex type / simple content.) If a type is nillable, the following functions will be generated:-
If a type is polymorphic, the following method is generated:
To set the choice option when marshaling, use one of the children's
If the complex type is an xs:choice, a
If the complex type is an xs:choice, the following method is also generated:
When a document has instances xs:anyAttribute it may be necessary to add extra namespace information to the
(See 2.5 - Interfacing with the Generated Classes for the naming of
1.4 - Evaluating LMX
To help with evaluation, LMX comes with a Windows based Evaluation Support Suite. This is a separate installation packaged as part of the main installation. The Evaluation Support Suite can be installed during the main LMX installation, or it can be installed at a later time by executing the Evaluation Support Suite installation from the LMX folder on the Windows Start menu.
-tframework flag on the command-line versions).
c_myClass::c_myClass( const c_myClass & rhs )) provide convenient examples.
1.5 - A Simple Example
We present here a simple example of the sort of code you'll write to use the LMX generated C++ code. While short, the example does illustrate the majority of the aspects of using LMX generated code. We first present the schema used by LMX to generate the code. In this case the generated code is stored in the files po.h and po.cpp. This is followed by an example XML instance for the schema. Finally, the code used to interface to the LMX generated classes is shown.
examples sub-directory of the installation or at http://www.codalogic.com/lmx/lmx-example.zip.
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:annotation>
<xsd:documentation xml:lang="en">
Purchase order schema for Example.com.
Copyright 2000 Example.com. All rights reserved.
</xsd:documentation>
</xsd:annotation>
<xsd:element name="purchaseOrder" type="PurchaseOrderType"/>
<xsd:element name="comment" type="xsd:string"/>
<xsd:complexType name="PurchaseOrderType">
<xsd:sequence>
<xsd:element name="shipTo" type="USAddress"/>
<xsd:element name="billTo" type="USAddress"/>
<xsd:element ref="comment" minOccurs="0"/>
<xsd:element name="items" type="Items"/>
</xsd:sequence>
<xsd:attribute name="orderDate" type="xsd:date"/>
</xsd:complexType>
<xsd:complexType name="USAddress">
<xsd:sequence>
<xsd:element name="name" type="xsd:string"/>
<xsd:element name="street" type="xsd:string"/>
<xsd:element name="city" type="xsd:string"/>
<xsd:element name="state" type="xsd:string"/>
<xsd:element name="zip" type="xsd:decimal"/>
</xsd:sequence>
<xsd:attribute name="country" type="xsd:NMTOKEN"
fixed="US"/>
</xsd:complexType>
<xsd:complexType name="Items">
<xsd:sequence>
<xsd:element name="item" minOccurs="0" maxOccurs="unbounded">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="productName" type="xsd:string"/>
<xsd:element name="quantity">
<xsd:simpleType>
<xsd:restriction base="xsd:positiveInteger">
<xsd:maxExclusive value="100"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:element>
<xsd:element name="USPrice" type="xsd:decimal"/>
<xsd:element ref="comment" minOccurs="0"/>
<xsd:element name="shipDate" type="xsd:date" minOccurs="0"/>
</xsd:sequence>
<xsd:attribute name="partNum" type="SKU" use="required"/>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
<!-- Stock Keeping Unit, a code for identifying products -->
<xsd:simpleType name="SKU">
<xsd:restriction base="xsd:string">
<xsd:pattern value="\d{3}-[A-Z]{2}"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:schema>
An XML Instance:
<?xml version='1.0'?>
<purchaseOrder orderDate='1999-10-20'>
<shipTo country='US'>
<name>Alice Smith</name>
<street>123 Maple Street</street>
<city>Mill Valley</city>
<state>CA</state>
<zip>90952</zip>
</shipTo>
<billTo country='US'>
<name>Robert Smith</name>
<street>8 Oak Avenue</street>
<city>Old Town</city>
<state>PA</state>
<zip>95819</zip>
</billTo>
<comment>Hurry, my lawn is going wild!</comment>
<items>
<item partNum='872-AA'>
<productName>Lawnmower</productName>
<quantity>1</quantity>
<USPrice>148.95</USPrice>
<comment>Confirm this is electric</comment>
</item>
<item partNum='926-AA'>
<productName>Baby Monitor</productName>
<quantity>1</quantity>
<USPrice>39.98</USPrice>
<shipDate>1999-05-21</shipDate>
</item>
</items>
</purchaseOrder>
And the example code to read the instance:
#include <iostream> // For std::cout etc.
#include "po.h" // Generated by LMX
#include "lmxparse.h" // For the LMX parser
int main()
{
// Allocate a place to store the an error code returned by
// the unmarshaling operation.
lmx::elmx_error l_error;
// c_root is the class generated by LMX that will store the
// unmarshalled XML. This constructor unmarshals the data
// stored in the file "po.xml" and places any error code in
// l_error.
c_root l_po( "po.xml", &l_error );
if( l_error != lmx::ELMX_OK )
{
std::cout << "An error occurred while reading XML\n";
return 0;
}
// Or we could have done:
// c_root l_po;
// lmx::elmx_error l_error = l_po.unmarshal( "po.xml" );
// if( l_error ...
// Interrogate the parsed XML
//---------------------------
if( l_po.getchosen() == c_root::e_purchaseOrder )
{
// Make some intermediate variables so that components are
// easier to reference
const c_PurchaseOrderType *lp_pot = &l_po.get_purchaseOrder();
if( lp_pot->isset_orderDate() ) // If the orderDate is set...
{
std::cout << "Ordered on: " <<
lp_pot->get_orderDate() << "\n";
if( lp_pot->get_orderDate().get_year() <= 2000 )
std::cout <<
"This was ordered last century - It is now overdue!\n";
}
// Reference another part of the data structure
const c_Items *lp_items = &lp_pot->get_items();
float l_total_cost = 0;
// Print the total number of items
std::cout << "Number of items = " <<
lp_items->size_item() << "\n";
// Print out some information about each order item
for( size_t l_i=0; l_i<lp_items->size_item(); ++l_i )
{
std::cout << "Item " <<
(l_i + 1) << ": " <<
lp_items->get_item( l_i ).get_quantity() <<
" off - " <<
lmx::as_ascii( lp_items->get_item( l_i ).get_productName() ) <<
" (" <<
lmx::as_ascii( lp_items->get_item( l_i ).get_partNum() ) << ")";
// The comment is optional, so only print it out if it is present
if( lp_items->get_item( l_i ).isset_comment() )
std::cout << " [" <<
lmx::as_ascii( lp_items->get_item( l_i ).get_comment() ) << "]";
std::cout << " - $" <<
lp_items->get_item( l_i ).get_USPrice() << "\n";
l_total_cost +=
lp_items->get_item( l_i ).get_USPrice().get_scaled( 2 );
}
std::cout << "Total Cost: $" << l_total_cost/100 << "\n";
std::cout << "Ship to: " <<
lmx::as_ascii( lp_pot->get_shipTo().get_name() ) << "\n";
std::cout << "Bill to: " <<
lmx::as_ascii( lp_pot->get_billTo().get_name() ) << "\n";
std::cout << "\n";
// Modify the XML
//---------------
c_root l_alt_po( l_po ); // We don't actually need to create
// a new instance before modifying
l_alt_po.get_purchaseOrder().get_items().append_item();
c_Items::c_item *lp_item =
&l_alt_po.get_purchaseOrder().get_items().back_item();
lp_item->set_productName( L"Fence" );
lp_item->set_quantity( 2 );
lp_item->set_partNum( L"100-AB" );
lp_item->set_USPrice( lmx::c_decimal( 12.95, 2U ) );
// We can also do:
// lp_item->set_USPrice( 12.95 ); // Although the schema needs to
// // specify the fraction digits
// // facet in order to get the number
// // of decimal places correct!
// lp_item->set_USPrice( lmx::c_decimal( 1295, 2 ) );
// // e.g. 1295 / (10^2)
// lp_item->set_USPrice( "12.95" );
lp_item->set_comment( L"Will this stop the baby getting on the lawn?" );
assert( lp_item->is_occurs_ok() ); // Check sufficient elements and
// attributes added
// Write the modified version of the XML to the file po-out.xml
//-------------------------------------------------------------
if( l_alt_po.marshal( "po-out.xml" ) == lmx::ELMX_OK )
std::cout << "Modified XML written successfully\n";
else
std::cout << "Error writing Modified XML\n";
}
return 0;
}
1.6 - Licensing & Purchase
For the purposes of evaluation, the LMX code generator can be used in a restricted mode without a license. In the restricted mode, the code generator limits the number of XML objects that it will compile.
1.7 - Version History
3.9
-naming YYY flag which specifies the naming convention to be used for names in the generated code. If camel is specified, LMX will attempt to convert names generated in the code to CamelCase. If underscore is specified, LMX will attempt to make names underscore separated.
-file-ext-snippets YYY flag.
c_soap and c_winhttp classes to aid implementing web services. See 3.6 - Use with Web Services for more information.
-alt-xml-reader and -alt-xml-writer flags. See the flags section for more information.
lmx::elmx_error enumeration has had added to it the enumeration values ELMX_USER_DEFINED_1 to ELMX_USER_DEFINED_9.
-lmx-include-path flag to allow specifying the directory where the LMX header files are stored.
-check-is-occurs-ok-on-marshal flag.
tlmx_uri_string typedef so that URI string can have an independent type to Unicode strings.
-autover-subst-groups flag.
-file-ext-cpp and -file-ext-h flags.
-local-enums flag.
xs:documentation elements) should be output in the .h and/or HTML file. See -doc-in-h and -doc-in-html flags.
-werror flag.
-no-root-class flag.
-no-container-ops flag.
-suffix-attribute, -suffix-element, -suffix-type and -suffix-group flags.
xmlns="" idiom according to XML Namespaces 1.1.
debug_error object to aid the debugging process.
-cpp-per-schema flag.
-snippets flag.
-no-marshal and -no-unmarshal.
2 - Quick Start
Having installed and licensed your LMX product, you will want to generate code. This section gives a brief introduction to this and will probably be sufficient for the majority of your use of the tool. For more detailed information, see section 3 - In More Depth.
2.1 - Code Generation
The LMX code generator can be run on Windows® and Linux platforms. On the Windows platform LMX is available as both a GUI and a command-line version.
2.1.1 - Code Generation Using the Windows Interface
The Windows® Interface version of LMX makes it easy to use the LMX tool. The Windows version is called WinLMX.
2.1.2 - Code Generation Using the Windows (DOS) Command Line Version
The LMX code generator can be run from a (DOS) command line prompt. This is useful when LMX is used as part of a batch build process such as a nightly build.
The basic command line syntax is:
lmx [flags] primary-xsd-file *[++ additional-global-xsd-file] *[+ additional-library-xsd-file] output-files-root
The flags are optional and are described in 3.4.1 - Command-line Flags.
primary-xsd-file is the name of the file that contains the main schema definition. See 3.3 - Selecting the Input File Type (XSD, WSDL, DTD) for more information on selecting between W3C Schema files, WSDL files and XML external DTD files.
+ character in front of the schema name as above, use the ++ character sequence. This can be useful when you are generating code for two or more schemas that share common imported schemas.
lmx my_xsd.xsd my_xsd_code
will compile the schema specified in my_xsd.xsd and generate the files my_xsd_code.h and my_xsd_code.cpp.
lmx my_xsd.xsd + my_xsd_lib_1.xsd + my_xsd_lib_2.xsd my_xsd_code
This will combine the schemas specified in my_xsd.xsd, my_xsd_lib_1.xsd and my_xsd_lib_2.xsd, and generate the files my_xsd_code.h and my_xsd_code.cpp.
lmx my_xsd_1.xsd ++ my_xsd_2.xsd + my_xsd_lib.xsd my_xsd_code
(N.B. notice the use of ++ rather than just +.) In this case, in addition to allowing global elements in my_xsd_1.xsd to be document level elements in an XML instance, global elements in my_xsd_2.xsd will also be treated as candidate document level elements.
2.1.3 - Code Generation Using the Linux Command Line Version
The operation of the Linux command-line version is the same as the Windows (DOS) command-line version as described in 2.1.2 - Code Generation Using the Windows (DOS) Command Line Version, except that the executable is called 'linmx'. Hence the basic command-line syntax becomes:
linmx [flags] primary-xsd-file *[++ additional-global-xsd-file] *[+ additional-library-xsd-file] output-files-root
For example:
linmx my_xsd.xsd my_xsd_code
linmx executable. For example:-
LMXDIR=/usr/local/bin/lmx
This enables the license file to be correctly located.
export LMXDIR
2.2 - C++ Compiling and Building
LMX by default generates all of the code into a single source file and header file so that the code is easy to manage from a build point of view. The idea is to generate the code, compile it and then leave it to one side, just linking with it when required.
lmxuser.h, lmxinternals.h, lmxtypes.h, lmxparse.h, lmxregex.h, and lmxunicode.h in suitable directories so that your C++ compiler can pick them up. During installation, these files are placed into the 'supporting-software/include' sub-directory of the installation. See 2.2.1 - #include files for information on how to include these files in the code that you write.
lmx-MT-vc9.lib for VC++ 9 (VS 2008), lmx-MT-vc8.lib for VC++ 8 (VS 2005), lmx-ML-vc71.lib for VC++ 7.1 (VS 2003), lmx-ML-vc6.lib for VC++ 6 or the DLL versions). These files, and other library / DLL versions of the supporting software are located in the sub-directories of the 'supporting-software' sub-directory of the installation. See 2.6 - Build Configurations for more information on this, including information on the Linux versions.
lmxtypes.cpp and lmxparse.cpp supplied as part of the Professional Edition using your C++ compiler. You will then need to link these objects with the objects compiled from the generated code and your code.
2.2.1 - #include files
When you write your code, you will need to #include appropriate .h files in your .cpp files in order to get access to the LMX generated code and the LMX parser.
Table 1: LMX Supporting Software Header Files
File Name Purpose lmxuser.h Collects together items that you may modify in order to change the behaviour of LMX. lmxinternals.h Collects together items that you should not modify. lmxtypes.h Header for the LMX schema types library (e.g. c_decimal etc). lmxparse.h Header for the LMX parser. lmxregex.h Header for the LMX regular expressions. lmxunicode.h Header for the LMX Unicode categories. lmxuser.h, lmxinternals.h, lmxtypes.h, lmxregex.h, and lmxunicode.h. For example, if the LMX generated header file is generated.h, then you will need to include the following lines in your .cpp file:
#include "generated.h"
If you perform marshaling and unmarshaling operations in your file, then you will need to include both the LMX generated .h file and the LMX parser header file; lmxparse.h. For example:
#include "generated.h"
#include "lmxparse.h"
If you choose to configure LMX to generate multiple header files (e.g. one header file per class), you should substitute the line #include "generated.h" mentioned above as appropriate.
2.3 - Unmarshaling (simple form)
Unmarshaling is the process of reading in XML and converting it to C++ objects. LMX can parse UTF-8, UTF-16 (big and little endian), ISO-10646-UCS-2 (big and little endian), ISO-8859-1 (aka Latin-1), and US-ASCII. The source XML can either be in an in-memory data buffer, or a file.
// Allocate somewhere to store any error code that the unmarshaling may
// produce
lmx::elmx_error result;
// Construct an object from the XML contained in the memory located at
// xml_message_data_buffer and store the result code of the unmarshaling in
// the 'result' variable.
const c_generated_xsd_root_class top_object( xml_message_data_buffer,
number_of_bytes_in_buffer,
&result );
// If the 'result' variable indicates that all is OK, interact with the
// object.
if( result == lmx::ELMX_OK )
{
// Work with unmarshalled data...
To read XML from a file, code similar to the following can be used:
// Allocate somewhere to store any error code that the unmarshaling may
// produce
lmx::elmx_error result;
// Construct an object from the XML contained in the file 'c:\myxml.xml'
// and store the result code of the unmarshaling in the 'result' variable.
const c_generated_xsd_root_class top_object( "c:\\myxml.xml", &result );
// If the 'result' variable indicates that all is OK, interact with the
// object.
if( result == lmx::ELMX_OK )
{
// Work with unmarshalled data...
// Create an instance of the class
c_generated_xsd_root_class top_object;
// ... additional code ...
// Use the unmarshal method, giving the details of the memory to read from
lmx::elmx_error result = top_object.unmarshal( xml_message_data_buffer,
number_of_bytes_in_buffer );
// If unmarshaling was successful, interact with the object
if( result == lmx::ELMX_OK )
{
// Work with unmarshalled data...
or, to read XML from a file:
// Create an instance of the class
c_generated_xsd_root_class top_object;
// ... additional code ...
// Use the unmarshal method, giving the details of the file to read from
lmx::elmx_error result = top_object.unmarshal( "c:\\myxml.xml" );
// If unmarshaling was successful, interact with the object
if( result == lmx::ELMX_OK )
{
// Work with unmarshalled data...
If you wish to unmarshal from an object for which the convenience functions were not generated, then you can use the unmarshal template functions that are defined in lmxparse.h. In this case the code form is - for XML in memory:
// Create an instance of the class
c_generated_class an_object;
// Use the template unmarshal method, giving the details of the memory to
// read from
lmx::elmx_error result = lmx::unmarshal( &an_object, xml_message_data_buffer,
number_of_bytes_in_buffer );
// If unmarshaling was successful, interrogate the object
if( result == lmx::ELMX_OK )
{
// Work with unmarshalled data...
or, to read XML from a file:
// Create an instance of the class
c_generated_class an_object;
// Use the unmarshal method, giving the details of the file to read from
lmx::elmx_error result = lmx::unmarshal( &an_object, "c:\\myxml.xml" );
// If unmarshaling was successful, interrogate the object
if( result == lmx::ELMX_OK )
{
// Work with unmarshalled data...
As mentioned previously, additional unmarshaling techniques are described in 3.1 - Unmarshaling (advanced forms).
2.4 - Marshaling (simple form)
Marshaling is the process converting the C++ object content into XML data.
// Create an instance of the class
c_generated_xsd_root_class top_object;
// ... Populate and manipulate top_object ...
// Allocate a string in which to store the marshalled XML
std::string my_string;
// Marshal the object to the string
top_object.marshal( &my_string );
// Create an instance of the class
c_generated_xsd_root_class top_object;
// ... Populate and manipulate top_object ...
// Marshal the object to the file "c:\myxml.xml"
if( top_object.marshal( "c:\\myfile.xml" ) != lmx::ELMX_OK )
{
// Something went wrong!
}
If you wish to marshal an object for which the convenience functions are not generated, then you can use the marshal template functions defined in lmxparse.h. In this case, the code for marshaling to string is:
// Create an instance of the class
c_generated_class an_object;
// ... Populate and manipulate an_object ...
// Allocate a string in which to store the marshalled XML
std::string my_string;
// Marshal the object to the string
lmx::marshal( an_object, &my_string );
// Create an instance of the class
c_generated_class an_object;
// ... Populate and manipulate an_object ...
// Marshal the object to the file "c:\myxml.xml"
if( lmx::marshal( an_object, "c:\\myfile.xml" ) != lmx::ELMX_OK )
{
// Something went wrong!
}
As mentioned previously, additional marshaling techniques are described in 3.2 - Marshaling (advanced forms).
2.5 - Interfacing with the Generated Classes
The C++ class interface to a generated item is independent of whether the item is an element or an attribute. Hence the difference between these two forms is abstracted away. The term 'item' is used here to refer to either an element or an attribute.
The other aspect that affects the interface is whether the item is:
NAME is used to represent the name given to the item.
xs:any will by default have the NAME part of the method name set to any. However, if there is more than one xs:any element in a complex type, or another element is named any, numbers will be appended to the name to provide differentiation.
2.5.1 - Singular Empty Type C++ Interface
No interface is generated.
2.5.2 - Optional Empty Type C++ Interface
If the item is not in an xs:choice, the following method is generated:
If the item is allowed in an xs:choice, but not present, the
void set_NAME();
NAME as present.
bool isset_NAME() const;
true if the item NAME is present, and false otherwise.
void unset_NAME();
NAME as not present.getchosen() method returns <class name>::e_choice_not_set.
2.5.3 - Singular Simple Type C++ Interface
Note that for the interface to list types, see Multiple Singular Types.
If the type has enumerations, the following additional methods are generated:
<return type> get_NAME() const;
NAME or a reference to NAME depending on the particular type.
void set_NAME( simple_type );
NAME to the specified value.
If a simple type is nillable, then it is treated as complex type / simple content. Thus the interaction with nillable types is discussed below.
elmx_enums getenum_NAME() const;
NAME.
bool setenum_NAME( elmx_enums );
NAME to the value corresponding to the specified enumerated constant. true is returned if the value is correctly set, and false otherwise.2.5.4 - Optional Simple Type C++ Interface
(Note: See Multiple Singular Types for the interface to list types.)
The Optional Simple Type has the same methods as defined for the Singular Simple Type, plus the following methods:
If the item is allowed in an xs:choice, but not present, the
bool isset_NAME() const;
true if the item NAME is present, and false otherwise.
void unset_NAME();
NAME as not present.getchosen() method returns <class name>::e_choice_not_set.
2.5.5 - Multiple Simple Type and List Simple Type C++ Interface
This interface is used for elements that have a cardinality greater than 1, and elements or attributes with lists.
If the type has enumerations, the following additional methods are generated:
size_t size_NAME() const;
NAME.
<return type> get_NAME( size_t n ) const;
NAME. The first instance is n=0.
lmx::elmx_error set_NAME( size_t n, type );
NAME items. The item previously at the n-th position is over-written. If a value of n is specified that is larger than the size of the collection, the append_NAME( type ) method is called. The first instance is n=0.
lmx::elmx_error append_NAME( type );
NAME items.
lmx::elmx_error insert_NAME( size_t n, type );
NAME items. The item previously at the n-th position is moved to the (n+1)-th position and so on. If a value of n is specified that is larger than the size of the collection, the append_NAME( type ) method is called. The first instance is n=0.
void delete_NAME( size_t n );
NAME. The first instance is n=0.
void clear_NAME();
If the type is an optional list, the following additional methods are generated (this allows an empty list to be present):
elmx_enums getenum_NAME( size_t n ) const;
NAME. The first instance is n=0.
bool setenum_NAME( size_t n, elmx_enums set );
NAME collection to the value corresponding to the specified enumeration constant. If a value of n is specified that is larger than the size of the collection, the appendenum_NAME( type ) method is called. The first instance is n=0. true is returned if the value is correctly set, and false otherwise.
bool appendenum_NAME( elmx_enums set );
NAME corresponding to the specified enumeration constant. true is returned if the value is correctly set, and false otherwise.
bool insertenum_NAME( size_t n, elmx_enums set );
NAME corresponding to the specified enumeration constant. If a value of n is specified that is larger than the size of the collection, the appendenum_NAME( type ) method is called. The first instance is n=0. true is returned if the value is correctly set, and false otherwise.
bool isset_NAME() const;
true if the item NAME is present, and false otherwise.
void unset_NAME();
NAME as not present.2.5.6 - Singular Complex Type C++ Interface
If the complex type is an xs:choice, the following method is generated:
<const ref to type> get_NAME() const;
NAME. It is recommended that this method be used for reading the item rather than the read/write variant.
<non-const ref to type> get_NAME();
NAME by returning a reference.
For the marshaling operation, the appropriate choice is selected when the relevant
<class name>::elmx_chosen getchosen() const;
set_NAME or non-const get_NAME method among the choice's children is called. For example, if CHOICE is a choice, represented by the class c_CHOICE, and OPTION is one of the elements within the choice, then if we have:
c_CHOICE &my_choice = ...;
we can select the desired option within the choice by doing:
my_choice.set_OPTION(...);
Similarly, if we do not immediately have a pointer or reference to c_CHOICE, we can set the desired option by doing:
my_item.get_CHOICE().set_OPTION(...);
If OPTION is a complex type which has associated with it a non-const get method, then the act of calling that non-const get method will cause the appropriate choice option to be selected. For example:
c_OPTION &my_option = my_item.get_CHOICE().get_OPTION(...);
or:
my_item.get_CHOICE().get_OPTION().set_an_option_child(12);
During marshaling, the order that the elements are output corresponds to the order in which they are written to the object.
<class name>::elmx_all getorder( size_t n ) const;
size_t getorder( elmx_all enumeration_const ) const;
xs:all construct of the element corresponding to the enumeration constant enumeration_const. The function returns lmx::k_all_order_not_present if the element is not present.
size_t getorder_NAME() const;
xs:all construct. The function returns lmx::k_all_order_not_present if the element is not present.
size_t sizeorder() const;
If a value is nillable, you should test
void setnil_NAME();
bool isnil_NAME() const;
true is a value is nil, and false otherwise.isnil_NAME() prior to attempting to read the element's body value(s) using get_NAME() etc.
void assign_NAME( c_NAME_TYPE *p_derived_type );
get_NAME() method can be used to access a polymorphic type, and no additional methods are generated for this purpose.)2.5.6.1 - Singular Complex Type Usage Examples
To read a child of NAME:
int an_int = top.get_NAME().get_CHILD();
Or, if you intend to read a lot of items from NAME, another possibility is:
const c_NAME & name_ref = top.get_NAME();
int an_int = name_ref.get_CHILD();
float a_float = name_ref.get_CHILD2();
To write to NAME:
top.get_NAME().set_CHILD( 12 );
Or:
c_NAME & name_ref = top.get_NAME();
name_ref.set_CHILD( 12 );
name_ref.set_CHILD2( 1.2 );
If the complex type is an xs:choice:
switch( top.getchosen() )
{
case c_top::e_my_int:
...
break;
case c_top::e_my_float:
...
break;
default:
assert(0);
}
An alternative to the above with better compile time checking is:
c_top::elmx_chosen chosen = top.getchosen();
if( chosen == c_top::e_my_int ) {
...
}
else if( chosen == c_top::e_my_float ) {
...
}
set or non-const get methods. For example:
top.get_NAME().set_CHILD( 12 );
Or:
c_NAME & name_ref = top.get_NAME();
name_ref.set_CHILD( 12 );
If the complex type is an xs:all:
for( size_t i=0; i<top.sizeorder(); ++i )
{
switch( top.getorder(i) )
{
case c_top::e_my_int:
...
break;
case c_top::e_my_float:
...
break;
default:
assert(0);
}
}
2.5.7 - Optional Complex Type C++ Interface
An Optional Complex Type has the same methods as a Singular Complex type, plus the additional methods specified for an Optional Simple Type.
getchosen() method is generated as described in Singular Complex Type. If the optional xs:choice is not present, the getchosen() method returns <class name>::e_choice_not_set.
2.5.7.1 - Optional Complex Type Usage Examples
To read an optional complex type:
if( top.isset_NAME() ) {
const c_NAME & name_ref = top.get_NAME();
int an_int = name_ref.get_CHILD();
float a_float = name_ref.get_CHILD2();
}
To write to an optional complex type:
c_NAME & name_ref = top.get_NAME();
name_ref.set_CHILD( 12 );
name_ref.set_CHILD2( 1.2 );
2.5.8 - Multiple Complex Type C++ Interface
Common to reading and writing:
For reading:
size_t size_NAME() const;
NAME.
For writing:
<const ref to type> get_NAME( size_t n ) const;
NAME. This version should always be used when reading from item NAME. The first instance is n=0.
If
void append_NAME();
NAME. This method is used in conjunction with the back_NAME() method. For more information see the example below.
<non-const ref to type> back_NAME();
NAME. This method is used in conjunction with the append_NAME() method. For more information, see the example below.
void insert_NAME( size_t n );
NAME items. The item previously at the n-th position is moved to the (n+1)-th position and so on. The first instance is n=0. If a value of n is specified that is larger than the size of the collection, the append_NAME() method is called. Use get_NAME( n ) to access and modify the inserted value.
<non-const ref to type> get_NAME( size_t n );
NAME. The first instance is n=0. If such an instance does not currently exist, an instance (and all intervening instances) will be created. For example, if m instances already exist, and instance n is requested (where m < n) n - m instances will be created.
<non-const ref to type> assign_NAME( size_t n, <const ref to type> );
void delete_NAME( size_t n );
NAME. The first instance is n=0.
void clear_NAME();
NAME is an optional xs:list, it also has the methods defined for an Optional Complex Type.
If a type is polymorphic, the following method is generated:
<class name>::elmx_chosen getchosen( size_t n ) const;
n parameter to select the index, the getchosen() method operates the same way as described in the Singular Complex Type section.
void append_NAME( c_NAME_TYPE *p_derived_type );
get_NAME(size_t n) method can be used to access a polymorphic type, and no additional methods are generated for this purpose.)2.5.8.1 - Multiple Complex Type Usage Examples
For reading:
for( size_t i=0; i<top.size_NAME(); ++i )
do_something( top.get_NAME( i ) );
For writing, a new instance of the complex type is appended using the append_NAME method. The new instance is then populated by repeated use of the back_NAME method. For example:
while( not_finished( &an_int, &a_float ) ) {
top.append_NAME();
top.back_NAME().set_int( an_int );
top.back_NAME().set_float( a_float );
}
2.5.9 - Multiple xs:anyAttribute C++ Interface
If xs:anyAttribute is specified, only the 'multiple' case is allowed.
c_xml_writer class. See 3.12 - Adding Extra Namespace Information for more information.
size_t sizeany_attribute() const;
void getany_attribute( size_t n,
std::string *p_namespace,
std::string *p_name,
std::string *p_value ) const;
void appendany_attribute( const std::string &name,
const std::string &value );
void insertany_attribute( size_t n,
const std::string &name,
const std::string &value );
appendany_attribute() method is called. The name of the attribute is stored in name, and the value in value. When written to the XML document, the value part will be surrounded by quote marks, and standard XML entities will be escaped (e.g. & ' " etc).
void deleteany_attribute( size_t n );
void clearany_attribute();
2.5.10 - Singular xs:any C++ Interface
When a document has instances xs:any it may be necessary to add extra namespace information to the c_xml_reader and c_xml_writer class. See 3.12 - Adding Extra Namespace Information for more information.
void get_any( std::string *p_namespace,
std::string *p_name,
std::string *p_value ) const;
xs:any element. If the xs:any element corresponds to:
then:
<nsp:myElement xmlns:nsp="http://t-k-w.com/example" etc....>...</nsp:myElement>
*p_namespace is set to: http://t-k-w.com/example
*p_name is set to: nsp:myElement
*p_value is set to: <nsp:myElement xmlns:nsp="http://t-k-w.com/example" etc....>...</nsp:myElement>.
*p_namespace and/or *p_name may be set to 0 (NULL) if it is not desired to retrieve their corresponding values.
xs:any access methods.)
void set_any( const lmx::tlmx_string &any );
xs:any element. The specified value must include the start tag and end tag, and be UTF-8 encoded. The value is inserted into the output using a simple copy operation. 2.5.11 - Optional xs:any C++ Interface
An Optional xs:any has the same interface methods as a Singular xs:any, plus the methods of any Optional Simple Type.
2.5.12 - Multiple xs:any C++ Interface
When a document has instances xs:any it may be necessary to add extra namespace information to the c_xml_reader and c_xml_writer class. See 3.12 - Adding Extra Namespace Information for more information.
size_t size_any() const;
xs:any.
void get_any( size_t n, std::string *p_namespace, std::string *p_name, std::string *p_value ) const;
xs:any. On completion of the function, the string pointed to by p_value will include the start tag and end tag. The first instance is n=0.
If the xs:any element corresponds to:
<nsp:myElement xmlns:nsp="http://t-k-w.com/example" etc....>...</nsp:myElement>
*p_namespace is set to: http://t-k-w.com/example
*p_name is set to: nsp:myElement
*p_value is set to: <nsp:myElement xmlns:nsp="http://t-k-w.com/example" etc....>...</nsp:myElement>.
*p_namespace and/or *p_name may be set to 0 (NULL) if it is not desired to retrieve their corresponding values.
(See 2.5 - Interfacing with the Generated Classes for the naming of xs:any access methods.)
void append_any( const lmx::tlmx_string &any );
xs:any. The specified value must include the start tag and end tag, and be UTF-8 encoded. The value is inserted into the output using a simple copy operation.
void insert_any( size_t n, const lmx::tlmx_string &any );
xs:any. The first instance is n=0. The item previously at the n-th position is moved to the (n+1)-th position and so on. If a value of n is specified that is larger than the size of the collection, the append_any() method is called. The specified value must include the start tag and end tag, and be UTF-8 encoded. The value is inserted into the output using a simple copy operation.
void delete_any( size_t n );
xs:any. The first instance is n=0.
void clear_any();
Each polymorphic class has the following public data member:
t_class_identity id
t_class_identity getid() const;
bool has_id( t_class_identity sought_id ) const;
c_base *p_base = &item.get_base();
if( p_base->getid() == c_derived::id )
{
c_derived *p_derived = dynamic_cast<c_derived *>( p_base );
}
Or:
c_base *p_base = &item.get_base();
if( p_base->has_id( c_derived::id ) )
{
c_derived *p_derived = dynamic_cast<c_derived *>( p_base );
// Do the common things specific to c_derived
}
if( p_base->has_id( c_more_derived::id ) )
{
c_more_derived *p_more_derived = dynamic_cast<c_more_derived *>( p_base );
// Do the common things specific to c_more_derived
}
clone() method. This returns a pointer to a deep copy of the object on which the clone() method is called. For example:
c_base *p_base = item.get_base().clone();
void reset();
One way to use this function is in a debug version
LMX also allows optional testing of facets when the various
Please see section 3.11.4 - Collecting Debug Error Information when using Convenience Methods for more information on these features.
Windows DLL declspecs can be enabled in the code by setting
If you do not have the source code version of the supporting software, LMX is supplied with a number of pre-compiled variants. The Win32 versions of the files are supplied in the
If you would like to use a DLL version of the LMX libraries, set
Note that these LMX DLLs link to the DLL based C run-time (MSVCRT.DLL - or MSVCRTD.DLL in the debug case), and you will need to re-distribute this with your code if you use these. Please contact us if you require LMX DLL versions that contain the necessary C run-time libraries statically linked into them.
The other
The <compiler> part of the name consists of either 'vc6' for Microsoft Visual Studio Version 6, 'vc71' for Microsoft Visual Studio Version 7.1 (aka MS VS 2003), 'vc8' for Microsoft Visual Studio Version 8 (aka MS VS 2005), or 'vc9' for Microsoft Visual Studio Version 9 (aka MS VS 2008).
Note that we have deprecated support for GCC 2.96. If you require libraries for this version of the compiler please contact us.
All libraries are built using Red Hat Fedora on an x86 architecture.
You can either use the Linux static libraries directly in your compilation commands (e.g. g++ ... liblmx410.a) or store the relevant library in a suitable library directory (e.g. use as g++ ... -L<lmx lib directory> -llmx410).
If you use a pre GCC v3 compiler, you may need to explicitly enable wide char support. This can be done by specifying the -D __GLIBCPP_WCHAR_SUPPORT__ compiler option; e.g.:
The complete abbreviated type notation has the form:
The
The
A pattern facet follows the Perl regular expression format without the beginning and end anchors. That is:
Length facets are documented for
The cardinality field specifies how many instances of the item are allowed. If the field is absent (including absence of the square brackets), one, and only one, occurrence of the item is allowed. If the field is present, the general format is:
To read XML from an in-memory buffer, code similar to the following can be used:
Marshaling is the process converting the C++ object content into XML data. An advanced section of marshaling code for writing the XML to a string looks as follows:
2.5.15 - Run-time Checking
LMX generates a function for each class that can be used to check whether sufficient attributes and elements have been set for a class to be minimally valid.
bool is_occurs_ok();
true if sufficient items have been set, and false otherwise.assert statement, e.g.:
c_NAME & name_ref = top.get_NAME();
name_ref.set_CHILD( 12 );
name_ref.set_CHILD2( 1.2 );
assert( name_ref.is_occurs_ok() );
set_NAME functions are called.
2.6 - Build Configurations
The LMX supporting software is supplied in a number of different forms to give you flexibility in the way you deploy it. The most flexible option is to have the source code version of the supporting software. This includes directives that allow the LMX run-time to be built into Windows DLLs. Alternatively, it can be statically built into Windows applications, or built into other platform applications (such as Linux).
#define LMX_WANT_DLL 1 in the USER: CTRL_DEFS section of lmxuser.h (or by modifying the project properties to define LMX_WANT_DLL accordingly). More flexible control of the declspecs can be done by modifying the #defines in the USER: DECL_DEFS section of lmxuser.h.
supporting-software/win32 sub-directory. The Linux library versions of the supporting software are supplied in the supporting-software/linux sub-directory of the installation. Cygwin libraries are supplied in the supporting-software/cygwin sub-diretcory.
2.6.1 - Win32 Libraries
The Win32 library files (located in the supporting-software/win32 sub-directory) with 'vc6' in the name are Microsoft VC++ 6 libraries. Files with 'vc71' in the name are for Microsoft VC++ 7.1 (aka Visual Studio .NET 2003). Files with 'vc8' in the name are Microsoft VC++ 8 (aka Visual Studio .NET 2005). Files with 'vc9' in the name are Microsoft VC++ 9 (aka Visual Studio .NET 2008). Files with 'x64' in the name are 64 bit versions. (We can provide VS 2002 binaries on request.)
#define LMX_WANT_DLL 1 in the USER: CTRL_DEFS section of lmxuser.h (or, better still, modify the project properties to define LMX_WANT_DLL accordingly), and use the DLL variant according to the following table:
Table 2: DLL Library Configurations
Compiler Type VC++ compiler switches LMX library files VC++ 9 (VS 2008) Multi-threaded DLL Debug /MDd lmx-MDd-vc9.dll, lmx-MDd-vc9.lib VC++ 9 (VS 2008) Multi-threaded DLL /MD lmx-MD-vc9.dll, lmx-MD-vc9.lib VC++ 8 (VS 2005) Multi-threaded DLL Debug /MDd lmx-MDd-vc8.dll, lmx-MDd-vc8.lib VC++ 8 (VS 2005) Multi-threaded DLL /MD lmx-MD-vc8.dll, lmx-MD-vc8.lib VC++ 7.1 (VS 2003) Multi-threaded DLL Debug /MDd lmx-MDd-vc71.dll, lmx-MDd-vc71.lib VC++ 7.1 (VS 2003) Multi-threaded DLL /MD lmx-MD-vc71.dll, lmx-MD-vc71.lib VC++ 6 Multi-threaded DLL Debug /MDd lmx-MDd-vc6.dll, lmx-MDd-vc6.lib VC++ 6 Multi-threaded DLL /MD lmx-MD-vc6.dll, lmx-MD-vc6.lib .lib files in the Win32 sub-directory allow Windows static builds. In this case, the file names have the form:
lmx-<build type>-<compiler>.lib
For example:
lmx-ML-vc71.lib
In each case, the <build-type> part of the library's file name corresponds to the compilation flag used in Visual C++. These are:
Table 3: LMX Library Types
Compiler Flag Meaning MDd Multi-thread DLL debug MD Multi-thread DLL
MLd Single-threaded debug
ML Single-threaded
MTd Multi-Thread debug
MT Multi-Thread
2.6.2 - Linux Libraries
The Linux libraries are located in the supporting-software/linux sub-directory. Select the library to use according to the following table:
Table 4: LMX GCC Library Types
Your GCC version LMX static version LMX shared version 3.2.x liblmx323.a liblmx323.so.?.? 3.3.x liblmx323.a liblmx323.so.?.? 3.4.x liblmx346.a liblmx346.so.?.? 4.x liblmx410.a liblmx410.so.?.? Other Contact us! Contact us!
g++ -D __GLIBCPP_WCHAR_SUPPORT__ <Your files> ...
2.6.3 - Cygwin Libraries
The file liblmx-gcc-cygwin.a (located in the supporting-software/cygwin sub-directory) is built using the GCC compiler running under the Cygwin environment.
2.7 - The Abbreviated Type Notation
In both the generated C++ header file and the HTML documentation file an abbreviated notation is used to describe the various types. The intention is to allow you to work mainly from the generated C++ header file or HTML file without having to refer to the actual schema definition, thus speeding up the development process. This section describes the format.
name_in_schema --> type { facets } [ cardinality ]
The name_in_schema field is the name of the item from the schema (possibly made ASCII safe). If the item is an xs:any element, then the field is set to {any}. If the item is the body of a Simple Content element, the field is set to {body}.
type is the type of the item. This may be one of the built-in schema types (e.g. xs:string) or the name of a type defined elsewhere in the schema. If the type is a built-in schema type, its name is prefixed by xs: irrespective of whether the schema namespace prefix has been set to xs.
facets field specifies (some of) the facets applied to a type by a schema. The facets are wrapped in curly braces and each facet description is separated by a comma. If there are no relevant facets, the facets field and the enclosing curly braces are omitted. Currently this field captures pattern, min/max and length facets. Enumeration facets are documented along with the methods for reading and writing items via enumeration (e.g. getenum_NAME and setenum_NAME).
/pattern/
The min and max range facets have the form:
min_value <= x < max_value
If either the min or the max limits are not specified, then the corresponding leg of the facet description is omitted. For example, if no min_value were specified, the following format would be used:
x < max_value
The comparison operator shown in the description (e.g. < or <=) depends on whether the facet is exclusive or inclusive.
xs:string, xs:hexBinary and xs:base64Binary types where specified. The facet is documented as:
min_length .. max_length
If the maximum length is unbounded, the following form is used:
min_length .. *
If the minimum length and the maximum length are the same, this is documented as:
length .. length
[ min .. max ]
where min specifies the minimum number of times the item can occur and max specifies the maximum number of times an item can occur. If min and max are the same values, then the following form is used:
[ min ]
If the maximum number of occurrences is unbounded, then the following form is used:
[ min .. * ]
2.7.1 - Abbreviated Type Notation Examples
To illustrate the Abbreviated Type Notation, the following examples are presented:
e1 --> xs:unsignedShort
e1 is an unsigned short that can occur once, and only once.
e2 --> xs:unsignedShort [0..1]
e2 is an optional unsigned short. It can occur zero or one time.
e3 --> xs:unsignedShort { 0<=x<1000 }
e3 is an unsigned short that is constrained to be greater than or equal to 0 and less than 1000. It can occur once, and only once.
e4 --> xs:unsignedShort { 0<=x<1000 }[0..1]
e4 is an unsigned short that is constrained to be greater than or equal to 0 and less than 1000. It can occur zero or one time.
e5 --> xs:unsignedShort { 0<x<=1000 }[1..*]
e5 is an unsigned short that is constrained to be greater than 0 and less than or equal to 1000. It can occur one or more times.
e6 --> xs:string { /a\d/,/b\d\d/ }[1..*]
e6 is a string that must match the pattern a\d or b\d\d (e.g. 'a1' or 'b12'). It can occur one or more times.
e7 --> xs:string { 1..5 }[1..*]
e7 is a string that can be between 1 and 5 characters long. It can occur one or more times.
3 - In More Depth
3.1 - Unmarshaling (advanced forms)
2.3 - Unmarshaling (simple form) describes the simple method for unmarshaling object content direct from files and memory. In certain situations more flexibility is required, and the following more advanced techniques may be required.
lmx::c_xml_reader_memory reader( xml_message_data_buffer,
number_of_bytes_in_buffer );
c_generated_xsd_root_class top_object;
lmx::elmx_error error = top_object.unmarshal( reader );
if( error == lmx::ELMX_OK )
{
const c_generated_xsd_root_class & const_top_object = top_object;
...
To read XML from a file, code similar to the following can be used:
lmx::c_xml_reader_file reader( "c:\\myxml.xml" );
if( low_level_reader.is_open() )
{
c_generated_xsd_root_class top_object;
lmx::elmx_error error = top_object.unmarshal( reader );
if( error == lmx::ELMX_OK )
{
const c_generated_xsd_root_class & const_top_object = top_object;
...
Looking more closely at the in-memory case, we first create an object that will do low-level read operations from memory. This is an instance of c_xml_reader_memory from the lmx namespace. The constructor for this object takes a pointer to the buffer as the first argument and the number of valid bytes in the buffer as the second argument.
lmx::c_xml_reader_memory low_level_reader( xml_message_data_buffer,
number_of_bytes_in_buffer );
We then create an instance of the top-level class generated by the LMX code generator. The name of this class will differ. If there is only one global element, the relevant class will be that of the global element. Alternatively, the name is either derived from the namespace prefix assigned to the schema's target namespace, or is c_root. It is normally the last class defined in the .h file.
c_generated_xsd_root_class top_object;
To do the unmarshaling, the unmarshal method of the top level object is called, giving it a reference to the low-level reader class. Note that you must always unmarshal into a freshly created object otherwise the results may be unpredictable. The method returns an error code.
lmx::elmx_error error = top_object.unmarshal( low_level_reader );
If the returned error code is lmx::ELMX_OK then unmarshaling has been successful, and the data structure can be interrogated. Section 3.5 - Error Codes lists other possible error codes.
if( error == lmx::ELMX_OK )
{
Having determined that the unmarshalled object is suitable for processing, it is advisable to create a const version of the class reference to avoid accidentally modifying the various objects.
const c_generated_xsd_root_class & const_top_object = top_object;
...
Unmarshaling from a file is a similar process, however in this case the c_xml_reader_file class from the lmx namespace is used as the type for the low-level reader. The constructor takes a single argument, which is the name of the file from which to read.
lmx::c_xml_reader_file reader( "c:\\myxml.xml" );
Having created the c_xml_reader_file instance, it is necessary to test whether the file has been successfully opened by calling the is_open method. (This method can also be called on an object of type c_xml_reader_memory, but this always returns true.)
if( reader.is_open() )
{
If the file is successfully opened, the unmarshaling operation continues as for the from memory case, e.g.:
c_generated_xsd_root_class top_object;
lmx::elmx_error error = top_object.unmarshal( reader );
if( error == lmx::ELMX_OK )
{
const c_generated_xsd_root_class & const_top_object = top_object;
...
Note that during unmarshaling memory may be dynamically allocated, which may cause exceptions to be thrown. Therefore it may be appropriate to include try/catch blocks at some level in your code.
3.1.1 - Unmarshaling with Additional Control when Reading XML Input
If additional control is required over the way XML is read, a further method for unmarshaling is available. The interface class for reading XML input is c_read, which is defined in lmxparse.h. To implement reading from different sources, or perform special processing while reading (such a reading from a socket or a compressed file), implement a concrete class that derives from c_read and implements the relevant virtual methods. Assuming such a concrete class is called c_my_reader, the code to unmarshal using this class would be:
c_my_reader low_level_reader( ...relevant parameters... );
lmx::c_xml_reader reader( low_level_reader );
c_generated_xsd_root_class top_object;
lmx::elmx_error error = top_object.unmarshal( reader );
if( error == lmx::ELMX_OK )
{
const c_generated_xsd_root_class & const_top_object = top_object;
...
The key part is creating an instance of the concrete class and then passing it to an instance of lmx::c_xml_reader. The rest of the unmarshaling operation is the same as described in 3.1 - Unmarshaling (advanced forms).
3.2 - Marshaling (advanced forms)
2.4 - Marshaling (simple form) describes the simple method for marshaling object content direct to files and memory. In certain situations more flexibility is required, and the following more advanced techniques may be required.
c_generated_xsd_root_class top_object;
// ... Populate and manipulate top_object ...
std::ostringstream sos;
lmx::c_xml_writer writer( sos );
top_object.marshal( writer );
std::string string_out( sos.str() );
Breaking this down into its component parts, we first create an instance of the top-level class generated by the LMX code generator. (See the description in section 2.3 - Unmarshaling (simple form) on the name given to the top level class.) This is populated either by interacting with the object's methods (see 2.5 - Interfacing with the Generated Classes), performing an unmarshaling operation as described in 2.3 - Unmarshaling (simple form) or a combination of the two.
The first part of the marshaling operation is to create an instance of a class that is derived from std::ostream. This is where the output will be written. The line below uses the class derived from std::ostream by the C++ library for outputting to a string, but the line std::ofstream sos; could equally be used for outputting to a file.
std::ostringstream sos;
Now create a low-level LMX XML writer object of type lmx::c_xml_writer, telling it to write using the sos object.
lmx::c_xml_writer writer( sos );
Tell the generated top-level object to marshal itself using the writer object by calling the top-level object's marshal method and giving it a reference to the writer low-level XML writer object:
top_object.marshal( writer );
Finally, for when writing to a string, get the output into a form where it is more readily usable:
std::string string_out( sos.str() );
string_out is then a std::string object containing the marshaled XML.
The equivalent code for writing to a file is as follows:
c_generated_xsd_root_class top_object;
// Open a file using the C++ class derived from ostream
// for writing to a file
std::ofstream fos( "c:\\myfile.xml" );
// If the file is opened successfully
if( fos.is_open() )
{
// Create an instance of