LMX
W3C XML Schema to C++ Data Binding

"LMX comes with very complete documentation."

Home 
LMX XML to C++ Binding Overview LMX C++ to XML Data Binding Video LMX C++ to XML Data Binding Features LMX Documentation XML XSD to C++ Data Binding Download LMX XML to C++ Data Binding Buy LMX XML to C++ Data Binding LMX Support XML to C++ Data Binding LMX C++ to XML Data Binding Features LMX XML to C++ Binding Examples
[Stand-alone HTML version] [PDF version]
Codalogic LMX
W3C XML Schema to C++ Code Generator

Version 3.8
Document Revision 2
Copyright © 2003-2008 Codalogic Ltd.

Introduction

This is the main documentation for the LMX XML Schema to C++ code generator. It is available as a Windows® HTML Help file (.chm) for viewing on Windows or as a single HTML file for easy printing and viewing on Linux.

If the LMX code generator seems like the thing 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.

Quick Index

Quick links to popular topics:

Contents

1 - Orientation
   1.1 - What is LMX?
   1.2 - Benefits of Using LMX
   1.3 - Download & Installation
   1.4 - Evaluating LMX
   1.5 - A Simple Example
   1.6 - Licensing & Purchase
   1.7 - Version History
2 - Quick Start
   2.1 - Code Generation
      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.2.1 - #include files
   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.5.14 - Resetting the Class
      2.5.15 - Run-time Checking
   2.6 - Build Configurations
      2.6.1 - Win32 Libraries
      2.6.2 - Linux Libraries
      2.6.3 - Cygwin Libraries
   2.7 - The Abbreviated Type Notation
      2.7.1 - Abbreviated Type Notation Examples
3 - In More Depth
   3.1 - Unmarshaling (advanced forms)
   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.4.1 - Command-line Flags
   3.5 - Error Codes
   3.6 - Use with Web Services
      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.6.2.4 - RESTful 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.10 - DTDs and Namespaces
   3.11 - Debugging and Handling Errors
      3.11.1 - Reporting 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.11.5 - Debugging Support
   3.12 - Adding Extra Namespace Information
   3.13 - Adding Schema Location Information
   3.14 - Adding an XMLDecl to the XML output
   3.15 - Pattern Facet Handling Customization
   3.16 - XML Output Format Customization
   3.17 - mustUnderstand / Comprehension Required
   3.18 - Finding an XML Instance's Namespace
   3.19 - Setting a Decimal Value With a Float
   3.20 - Test Framework Generation
   3.21 - LMX Extensions
   3.22 - Coding Convention
4 - Reference
   4.1 - LMX Compound Types

[Top] [Contents]

1 - Orientation

This section will give you an overview of what LMX does, and describes how to get ready to use LMX, including how to install and license LMX.

[Top] [Contents]

1.1 - What is LMX?

LMX is a W3C XML Schema to C++ code generator. It takes a W3C XML Schema and generates C++ code that allows you to read and write XML conforming to the Schema by interacting with the C++ classes/objects generated by the tool.

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.

[Top] [Contents]

1.2 - Benefits of Using LMX

  • Quick and easy to manipulate XML data.

  • Knowledge of XML and XML Schema not required - You can simply use the documentation in the generated header file.

  • The names of variables are checked at compile time - Simple typos in names are detected automatically and early.

  • No programming of tedious state code as required with SAX (and StAX) - LMX does all that in a couple of seconds (or less!)

  • Beneficial for both small and large schema.

  • Easy to implement web services.

  • Can read and generate XML fragments.

  • Simple to use - A minimal number of source files so that you don't need to spend time managing code.

  • Data types can be customized using C++ language syntax - No need to learn complex code generator configuration options, and less chance of introducing accidental differences between builds by using the wrong configuration. (See table below.)

  • Support for SOAP 'mustUnderstand' attribute detection.

  • Easy to switch between wide character strings and 'narrow' character strings.

  • Exception safe design.

  • Portable code - Run on different platforms. Even build and test on one platform and run on another.

[Top] [Contents]

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.

[Top] [Contents]

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.

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 -tframework flag on the command-line versions).

If you would like to see examples of how to use the generated code, then the generated copy constructors (of the form c_myClass::c_myClass( const c_myClass & rhs )) provide convenient examples.

A worked example is also available at http://www.codalogic.com/lmx/examples.php to further help with the evaluation process.

[Top] [Contents]

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.

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 examples sub-directory of the installation or at http://www.codalogic.com/lmx/lmx-example.zip.

The schema:

    <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;
    }

[Top] [Contents]

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.

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/

[Top] [Contents]

1.7 - Version History

3.8
  • Added c_soap and c_winhttp classes to aid implementing web services. See 3.6 - Use with Web Services for more information.
  • Added the ability for users to customize the behavior of generated classes using snippet event handlers. These call user defined methods during the marshal and unmarshal processes. See 3.9.1 - Augmenting Generated Classes with Snippet Event Handlers for more information.
  • Added the -alt-xml-reader and -alt-xml-writer flags. See the flags section for more information.
  • To provide custom error feedback, primarily for snippet event handlers, the lmx::elmx_error enumeration has had added to it the enumeration values ELMX_USER_DEFINED_1 to ELMX_USER_DEFINED_9.
  • Added -lmx-include-path flag to allow specifying the directory where the LMX header files are stored.
  • Added the -check-is-occurs-ok-on-marshal flag.
  • Added tlmx_uri_string typedef so that URI string can have an independent type to Unicode strings.
  • Added #ifndef LMX_NO_WSTRING sections to the supporting software source code to allow conditional removal of std::wstring from a build.

3.7.1

  • Fixed handling of hierarchies of substitution groups.
  • Auto-versioning of substitution groups now has an explicit option. Previously auto-versioning of substitution groups was the default (but non-standard) behavior. See -autover-subst-groups flag.

3.7

  • Added options to specify the file extensions of the generated files. See -file-ext-cpp and -file-ext-h flags.
  • Added option to specify that the C++ enums associated with schema enumeration facets should be local to a class. See -local-enums flag.
  • Added options to specify that documentation from the schema (included in xs:documentation elements) should be output in the .h and/or HTML file. See -doc-in-h and -doc-in-html flags.
  • A flag has been added to cause any warnings generated during code generation to return the same program error code returned by errors. See the -werror flag.
  • The Windows DLLs now have version information in them.
  • A bug associated with determining the used XML namespaces has been fixed.
  • Added option to suppress generation of root class. See -no-root-class flag.
  • Added option to not generate code for insert/delete/clear operations on items that can occur more than once. See -no-container-ops flag.

3.6

  • Added options to specify suffixes to be added to the code names of attributes, elements, types, and groups. See -suffix-attribute, -suffix-element, -suffix-type and -suffix-group flags.
  • Corrected handling of xmlns="" idiom according to XML Namespaces 1.1.
  • Added static debug_error object to aid the debugging process.
  • Added one .cpp per schema file generation mode. See -cpp-per-schema flag.
  • Added ability for developers to augment the generated classes with their own code. See -snippets flag.
  • More accurate HTML documentation generation.

3.5

  • Added options to control whether only marshaling or only unmarshaling code is generated. See flags -no-marshal and -no-unmarshal.

3.4

  • Added polymorphic behavior for XSD extension and restriction types. Use –no-polymorphic to switch off polymorphic behavior.

3.3

  • Adopted the Windows XP look-and-feel for WinLMX.
  • Corrected some issues with simpleContent types.
  • Changed company name.

3.2.5

  • Fixed a dependency issue in the generated C++ code between C++ declarations and definitions for attributes in restricted simple content.

3.2.4

  • Improved support for QNames.

3.2.3

  • Improved support for multiple instances of simple type lists.
  • Fixed handling of fractional seconds less than 10.0 in time, datetime and duration types.

3.2

  • Further enhancements to WinLMX's method prototype viewing feature made.
  • Improvements in the way xs:include files are handled.
  • Corrected handling of multiple occurrences of information items with fixed or default value constraints.

3.1

  • Further enhancements to WinLMX's method prototype viewing feature made.
  • Fixed an XML namespace handling issue.
  • Modified c_read_file so that reading XML from files is faster in applications generated with Visual Studio 2005.
  • Added the ability to set schema location attributes in generated XML.
  • Formerly the validation of the length facets for xs:string types was only correct if they were mapped to std::wstring. This has now been corrected so that the validation is correct irrespective of whether the xs:string types are mapped to std::wstring or std::string.

3.0

  • WinLMX enhanced. The base names of variables and methods can now be edited using the interface.
  • Editing of a schema can be invoked from WinLMX.
  • The generated HTML documentation can be displayed from WinLMX.

3.0 Beta

  • XML Parser refactored.
  • Additional marshal and unmarshal methods generated to make it more convenient to marshal and unmarshal to/from files and memory.
  • Test framework output (-tframework) simplified.
  • WinLMX enhanced.
  • If multiple schemas are treated as global, each such schema has its own XML namespace URI to namespace prefix map.

2.11

  • Nested C++ namespaces can be specified.
  • Removed use of global_event_map in 'global' unmarshal functions.
  • Added provision for extension from xs:anyType.
  • Improved parser efficiency.
  • User has control over XML namespace prefixes used via -ns-prefix-map command-line flag.

2.10

  • Primarily an upgrade to support the Basic Edition license.
  • Includes code for VS2005 libraries.
  • Enforce the "C" locale for string to float conversions in locale aware implementations.
  • Unicode handling extended beyond the Basic Multi-lingual Plane to full Unicode range (0x-0x10ffff).
  • Moved position of generated braces!

2.9

  • Improved 'One .h/.cpp file per class' handling.
  • insert_NAME() methods generated for interfacing with collections.
  • Mapping of namespace prefix flags supported (see -ns-prefix-map).
  • Improved mapping of punctuation characters in enumeration identifiers to C++ names.
  • Unions are no longer anonymous, leading to improved portability on compilers such as HP-UX aCC.

2.8

  • Can specify wide-char file names when opening XML files for reading.
  • Added string converters for std::string to std::string and std::wstring to std::wstring. These non-converters make it easier to operate in a environment where _TCHAR type mechanisms are being used.
  • Added features to help MS IntelliSense.
  • Added work-arounds for IBM Visual Age Compiler.

2.7

  • Code can be generated from an XML external DTD definition - see 3.3 - Selecting the Input File Type (XSD, WSDL, DTD).
  • A W3C Schema embedded in a WSDL file can be automatically parsed - see 3.3 - Selecting the Input File Type (XSD, WSDL, DTD).
  • Modified header file layout so that it is possible to switch between wide and narrow strings without requiring the supporting software source code.
  • Fixed a bug in the generated code that did a double memory allocation when calling c_class &get_class( size_t index ), causing a memory leak.
  • Fixed problem in the generated code when parsing XML instances from schemas that have multiple global elements and have multiple namespaces associated with them.
  • Added lmx_assert() macro to allow customisation of how assertions are handled. The macro is currently mapped directly to assert(), but could be modified to allow throwing exceptions in release code if desired.
  • Character entities in entity definitions are now expanded immediately when first parsing, rather than when the entity text is used.

2.6

  • Modified constructor initialisation order to remove GCC -Wall warnings.
  • Added at() and get( unsigned char [], size_t ) methods to c_binary.
  • Added functions to convert between wide and narrow strings (and vice versa).
  • Added some defaults to switch statements to address compiling with GCC -Wall.
  • Fixed problem with single quote appearing in attribute values when parsing xs:any elements.
  • Fixed problem with comments that contained no space after the <!-- sequence and contained a / character (e.g. <!--My comment with / char -->).

2.5

  • Customized naming of methods now supported - see 3.7 - Naming of Methods and Variables.
  • Union simpleType as base of simpleContent now supported.
  • Improve recording of code generator options in generated code when WinLMX used.
  • c_xml_reader destructor made virtual and unused variable removed from c_big_int::operator >.

2.4

  • Added -multi-file flag to allow one class per file operation.
  • WinLMX allows saving of default project settings for easier, more consistent project start up.
  • Fixed issue with -output-defaults.

2.3

  • Added -no-nested-classes flag to allow specifying that nested classes should not be used.

[Top] [Contents]

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.

[Top] [Contents]

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.

The generated code is cross-platform.

[Top] [Contents]

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.

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.

[Top] [Contents]

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-xsd-file] *[+ additional-xsd-file] output-files-root
The flags are optional and are described in 3.4.1 - Command-line Flags.

The 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.

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 + 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.

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:

    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.

If my_xsd.xsd refers to other schemas, an example command line would be:

    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.

If two schemas refer to a common schema, one way to generate code is to use a command line similar to:

    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.

[Top] [Contents]

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'.

When using a licensed Linux version it is necessary to set and export the LMXDIR environment variable to the directory containing the linmx executable. For example:-

    LMXDIR=/usr/bin/lmx export LMXDIR
This enables the license file to be correctly located.

[Top] [Contents]

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.

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 lmxuser.h, lmxinternals.h, lmxtypes.h and lmxparse.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.

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 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.

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 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.

[Top] [Contents]

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.

In addition to the header files generated by LMX, the LMX supporting software contains further header files. These are:

File NamePurpose
lmxuser.hCollects together items that you may modify in order to change the behaviour of LMX.
lmxinternals.hCollects together items that you should not modify.
lmxtypes.hHeader for the LMX schema types library (e.g. c_decimal etc).
lmxparse.hHeader for the LMX parser.
Table 1: LMX Supporting Software Header Files

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 lmxuser.h, lmxinternals.h and lmxtypes.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.

[Top] [Contents]

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.

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:

    // 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...

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:

    // 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).

[Top] [Contents]

2.4 - Marshaling (simple form)

Marshaling is the process converting the C++ object content into XML data.

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:

        // 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 );

The equivalent code for writing to a file is as follows:

        // 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 );

and the code for marshaling to a file is:

        // 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).

[Top] [Contents]

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.

An interface to an item depends in one respect on whether an item is:

  • empty.

  • a simpleType.

  • a complexType.

  • xs:anyAttribute.

  • xs:any.

The other aspect that affects the interface is whether the item is:
  • Singular - minOccurs = maxOccurs = 1, or a required attribute.

  • Optional - minOccurs = 0, maxOccurs = 1, or a normal attribute.

  • Multiple - maxOccurs > 1 (or unbounded), or a list.

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 NAME is used to represent the name given to the item.

An element of type 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.

[Top] [Contents]

2.5.1 - Singular Empty Type C++ Interface

No interface is generated.

[Top] [Contents]

2.5.2 - Optional Empty Type C++ Interface

If the item is not in an xs:choice, the following method is generated:
void    set_NAME();
Marks the item NAME as present.

bool    isset_NAME() const;
Returns true if the item NAME is present, and false otherwise.

void    unset_NAME();
Marks the item NAME as not present.

If the item is allowed in an xs:choice, but not present, the getchosen() method returns <class name>::e_choice_not_set.

[Top] [Contents]

2.5.3 - Singular Simple Type C++ Interface

Note that for the interface to list types, see Multiple Singular Types.
<return type>  get_NAME() const;
Return either the value of item NAME or a reference to NAME depending on the particular type.

void            set_NAME( simple_type );
Set item NAME to the specified value.

If the type has enumerations, the following additional methods are generated:
elmx_enums      getenum_NAME() const;
Returns the enumerated constant corresponding to the value of item NAME.

bool            setenum_NAME( elmx_enums );
Sets the value of item NAME to the value corresponding to the specified enumerated constant. true is returned if the value is correctly set, and false otherwise.

If a simple type is nillable, then it is treated as complex type / simple content. Thus the interaction with nillable types is discussed below.

[Top] [Contents]

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 not in an xs:choice, the following methods are generated:

bool    isset_NAME() const;
Returns true if the item NAME is present, and false otherwise.

void    unset_NAME();
Marks the item NAME as not present.

If the item is allowed in an xs:choice, but not present, the getchosen() method returns <class name>::e_choice_not_set.

[Top] [Contents]

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.
size_t    size_NAME() const;
Returns the number of instances to type NAME.

<return type> get_NAME( size_t n ) const;
Returns the n-th value of item NAME. The first instance is n=0.

lmx::elmx_error  set_NAME( size_t n, type );
Sets the instance at the n-th position of the collection of 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 );
Appends an instance to the collection of NAME items.

lmx::elmx_error  insert_NAME( size_t n, type );
Inserts an instance into the n-th position of the collection of 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 );
Deletes the n-th value of item NAME. The first instance is n=0.

void          clear_NAME();
Removes all items from the collection.

If the type has enumerations, the following additional methods are generated:
elmx_enums   getenum_NAME( size_t n ) const;
Returns the enumeration constant corresponding to the n-th instance of item NAME. The first instance is n=0.

bool         setenum_NAME( size_t n, elmx_enums set );
Sets the value of the item at the n-th position of the 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 );
Appends a value to the item 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 );
Inserts a value at the n-th position of the collection 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.

If the type is an optional list, the following additional methods are generated (this allows an empty list to be present):
bool    isset_NAME() const;
Returns true if the item NAME is present, and false otherwise.

void    unset_NAME();
Marks the item NAME as not present.

[Top] [Contents]

2.5.6 - Singular Complex Type C++ Interface

<const ref to type>       get_NAME() const;
This method provides read-only access to 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();
This method allows read/write access to NAME by returning a reference.

If the complex type is an xs:choice, the following method is generated:
<class name>::elmx_chosen   getchosen() const;
Returns the class specific enumerated value corresponding to the present choice.

For the marshaling operation, the appropriate choice is selected when the relevant 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);

If the complex type is an xs:all, the following methods are generated:

<class name>::elmx_all   getorder( size_t n ) const;
Returns the enumeration constant corresponding to the n-th present element in the xs:all construct. The first element corresponds to n == 0.

size_t                   getorder( elmx_all enumeration_const ) const;
Returns the position within the 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;
Returns the position of element NAME within the xs:all construct. The function returns lmx::k_all_order_not_present if the element is not present.

size_t                   sizeorder() const;
Returns the number of elements present in the xs:all construct.

During marshaling, the order that the elements are output corresponds to the order in which they are written to the object.

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:-

void                     setnil_NAME();
Used to set the element to a nil value.

bool                     isnil_NAME() const;
Returns true is a value is nil, and false otherwise.

If a value is nillable, you should test isnil_NAME() prior to attempting to read the element's body value(s) using get_NAME() etc.

If a type is polymorphic, the following method is generated:

void                     assign_NAME( c_NAME_TYPE *p_derived_type );
Sets the polymorphic type associated with item NAME to the object pointed to by p_derived_type, which must be a derived type of c_NAME_TYPE. Once the pointer is assigned to the generated object, the generated object takes responsibility for deleting the pointer on destruction. (Note: the get_NAME() method can be used to access a polymorphic type, and no additional methods are generated for this purpose.)

[Top] [Contents]

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 ) {
        ...
    }

To set the choice option when marshaling, use one of the children's 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);
            }
        }

[Top] [Contents]

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.

If the complex type is an xs:choice, a 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.

[Top] [Contents]

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 );

[Top] [Contents]

2.5.8 - Multiple Complex Type C++ Interface

Common to reading and writing:
size_t        size_NAME() const;
Returns the number of instances of item NAME.

For reading:
<const ref to type>      get_NAME( size_t n ) const;
Returns a const reference to the n-th instance of item NAME. This version should always be used when reading from item NAME. The first instance is n=0.

For writing:
void                     append_NAME();
Appends a new instance of item 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();
Returns a reference to the instance at the back of the list of item 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 );
Inserts an instance into the n-th position of the collection of 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 );
Returns a reference to the n-th instance of item 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> );
Does a deep copy of the input parameter to the n-th position of the collection. 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.

void                     delete_NAME( size_t n );
Deletes the n-th instance of item NAME. The first instance is n=0.

void                     clear_NAME();
Removes all items from the collection.

If NAME is an optional xs:list, it also has the methods defined for an Optional Complex Type.

If the complex type is an xs:choice, the following method is also generated:

<class name>::elmx_chosen   getchosen( size_t n ) const;
Returns the class specific enumerated value corresponding to the chosen element in the n-th instance of the xs:choice. n=0 specifies the first instance. Other than the n parameter to select the index, the getchosen() method operates the same way as described in the Singular Complex Type section.

If a type is polymorphic, the following method is generated:
void                     append_NAME( c_NAME_TYPE *p_derived_type );
Appends the polymorphic type pointed to by p_derived_type to the item NAME, which must be a derived type of c_NAME_TYPE. Once the pointer is assigned to the generated object, the generated object takes responsibility for deleting the pointer on destruction. (Note: the get_NAME(size_t n) method can be used to access a polymorphic type, and no additional methods are generated for this purpose.)

[Top] [Contents]

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 );
    }

[Top] [Contents]

2.5.9 - Multiple xs:anyAttribute C++ Interface

If xs:anyAttribute is specified, only the 'multiple' case is allowed.

When a document has instances xs:anyAttribute it may be necessary to add extra namespace information to the c_xml_writer class. See 3.12 - Adding Extra Namespace Information for more information.

size_t sizeany_attribute() const;
Returns the number of xs:anyAttributes present.

void getany_attribute( size_t n, std::string *p_namespace, std::string *p_name, std::string *p_value ) const;
Retrieves the n-th xs:anyAttribute. The namespace of the attribute (e.g. 'http://...') is stored in the string pointed to by p_namespace, the name of the attribute is stored in the string pointed to by p_name, and the value is stored in the string pointed to by p_value. n=0 refers to the first instance.

void appendany_attribute( const std::string &name, const std::string &value );
Appends an xs:anyAttribute to the list of xs:anyAttributes. 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 insertany_attribute( size_t n, const std::string &name, const std::string &value );
Inserts an xs:anyAttribute at the n-th position of the list of xs:anyAttributes. 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 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 );
Deletes the n-th instance of xs:anyAttribute. n=0 refers to the first instance.

void clearany_attribute();
Removes all items from the collection.

[Top] [Contents]

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;
Retrieves the details of an xs:any element. If the xs:any element corresponds to:

    <nsp:myElement xmlns:nsp="http://t-k-w.com/example" etc....>...</nsp:myElement>

then:
  • *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 set_any( const lmx::tlmx_string &any );
Set the value of an 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.

[Top] [Contents]

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.

[Top] [Contents]

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;
Returns the number of instances of a particular xs:any.

void      get_any( size_t n, std::string *p_namespace, std::string *p_name, std::string *p_value ) const;
Retrieves the details of n-th element of a particular 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>

then:
  • *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 );
Appends an instance to a particular 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 );
Inserts an instance to the n-th position of a particular 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 );
Deletes the n-th instance of a particular xs:any. The first instance is n=0.

void      clear_any();
Removes all items from the collection.

[Top] [Contents]

2.5.13 - Additional Polymorphic Methods

[Top] [Contents]

2.5.13.1 - Finding the Identity of a Polymorphic Class

One way to determine the type of a polymorphic class is to use C++'s RTTI. Additionally, LMX generates methods and members that allow the identity of a class to be determined.

Each polymorphic class has the following public data member:

t_class_identity id
Returns a constant that is unique for the particular class.

Each polymorphic class has the following public methods:
t_class_identity  getid() const;
A virtual function that returns the id constant that identifies the class type.

bool              has_id( t_class_identity sought_id ) const;
A virtual function that returns true if the class hierarchy contains the type corresponding to sought_id.

For example:
    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
    }

[Top] [Contents]

2.5.13.2 - Polymorphic Cloning

To support polymorphic object duplication, LMX generates a virtual 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();

[Top] [Contents]

2.5.14 - Resetting the Class

Each class has a function that resets the object to the state it would be after construction. The prototype for this function is:
void    reset();
Resets the state of the object to that as if it had just been constructed.

[Top] [Contents]

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();
Tests whether sufficient elements and attributes have been set for the class to be valid. Returns true if sufficient items have been set, and false otherwise.

One way to use this function is in a debug version 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() );

LMX also allows optional testing of facets when the various set_NAME functions are called.

Please see section 3.11.4 - Collecting Debug Error Information when using Convenience Methods for more information on these features.

[Top] [Contents]

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).

Windows DLL declspecs can be enabled in the code by setting #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.

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 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.

[Top] [Contents]

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.)

If you would like to use a DLL version of the LMX libraries, set #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:

CompilerTypeVC++ compiler switchesLMX library files
VC++ 9 (VS 2008)Multi-threaded DLL Debug/MDdlmx-MDd-vc9.dll, lmx-MDd-vc9.lib
VC++ 9 (VS 2008)Multi-threaded DLL/MDlmx-MD-vc9.dll, lmx-MD-vc9.lib
VC++ 8 (VS 2005)Multi-threaded DLL Debug/MDdlmx-MDd-vc8.dll, lmx-MDd-vc8.lib
VC++ 8 (VS 2005)Multi-threaded DLL/MDlmx-MD-vc8.dll, lmx-MD-vc8.lib
VC++ 7.1 (VS 2003)Multi-threaded DLL Debug/MDdlmx-MDd-vc71.dll, lmx-MDd-vc71.lib
VC++ 7.1 (VS 2003)Multi-threaded DLL/MDlmx-MD-vc71.dll, lmx-MD-vc71.lib
VC++ 6Multi-threaded DLL Debug/MDdlmx-MDd-vc6.dll, lmx-MDd-vc6.lib
VC++ 6Multi-threaded DLL/MDlmx-MD-vc6.dll, lmx-MD-vc6.lib
Table 2: DLL Library Configurations

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 .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:

Compiler FlagMeaning
MDdMulti-thread DLL debug
MDMulti-thread DLL
MLdSingle-threaded debug
MLSingle-threaded
MTdMulti-Thread debug
MTMulti-Thread
Table 3: LMX Library Types

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).

[Top] [Contents]

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:

Your GCC versionLMX static versionLMX shared version
3.2.xliblmx323.aliblmx323.so.?.?
3.3.xliblmx323.aliblmx323.so.?.?
3.4.xliblmx346.aliblmx346.so.?.?
4.xliblmx410.aliblmx410.so.?.?
OtherContact us!Contact us!
Table 4: LMX GCC Library Types

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.:

    g++ -D __GLIBCPP_WCHAR_SUPPORT__ <Your files> ...

[Top] [Contents]

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.

[Top] [Contents]

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.

The complete abbreviated type notation has the form:

    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}.

The 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.

The 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).

A pattern facet follows the Perl regular expression format without the beginning and end anchors. That is:

    /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.

Length facets are documented for 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

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:

    [ 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 .. * ]

[Top] [Contents]

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.

[Top] [Contents]

3 - In More Depth

[Top] [Contents]

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.

To read XML from an in-memory buffer, code similar to the following can be used:

    lmx::c_xml_reader_memory low_level_reader( xml_message_data_buffer, 
                                        number_of_bytes_in_buffer );

    c_generated_xsd_root_class top_object;

    lmx::elmx_error error = top_object.unmarshal( low_level_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 low_level_reader( "c:\\myxml.xml" );

    if( low_level_reader.is_open() )
    {
        c_generated_xsd_root_class top_object;

        lmx::elmx_error error = top_object.unmarshal( low_level_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 low_level_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( low_level_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( low_level_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.

[Top] [Contents]

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.

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:

        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 an LMX writer class,
            // giving it a reference to the opened file's
            // stream
            lmx::c_xml_writer writer( fos );

            // Tell the top_object to marshal its contents
            // using the LMX writer class
            top_object.marshal( writer );
        }
At the end of the above operations, the file c:\myfile.xml will contain the marshaled XML.

[Top] [Contents]

3.3 - Selecting the Input File Type (XSD, WSDL, DTD)

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.

If the file extension is '.wsdl', LMX with attempt to locate a schema within a WSDL file and parse that. Note that LMX will only extract the first schema within a WSDL file. If multiple schema are present in a WSDL