LMX
W3C XML Schema to C++ Data Binding

XML C++ data binding made easy

Home
Have a question? E-mail
LMX XSD XML C++ Binding Overview  Overview  Download LMX XML C++ Data Binding  Download  Buy LMX XML C++ Data Binding  Buy  LMX Documentation XSD XML C++ Data Binding  Documentation  LMX C++ XML XSD Code Generator Quick Intro  Walkthrough  LMX C++ XML Data Binding Try On-line  Try On-line  LMX XML XSD C++ Binding Support  Support 

Codalogic LMX XML C++ Databinder Example

We show here an example of how to use LMX for XML to C++ data binding. Although it is brief, it covers many of the XML C++ databinding concepts, such as marshaling / unmarshaling XML and how to interface with optional, singular and multiple types. It also shows how to work with XML schema complex types.

After trying this example, you may also like to download the XML SOAP example whichs builds on the example described here.

Example

The example is based on one of the Purchase Order schemas in the XML Schema Primer specification. It shows how to generate C++ code from the XML schema, and how to use the generated C++ binding code.

To follow the example you should download the following ZIP file: lmx-example.zip. If you have not done so already, you can also download the LMX XML C++ Databinder install file to generate the po.h and po.cpp C++ files. Alternatively you can use the pre-generated files contained in the ZIP file.

Having downloaded the relevant files you should perform the following steps:

  1. Unzip the example file into a fresh directory.
  2. If you wish to generate the po.h and po.cpp C++ files using LMX, double-click on the file po.lmxprj in Explorer. This should bring up WinLMX. Within WinLMX, press the 'Compile...' button at the bottom of the window and the files will be generated. (Press 'Exit' when complete.)

    (Some example sections from the generated po.cpp C++ file are shown below.)

  3. Or, you can copy the pre-generated C++ files located in the Generated sub-directory into the example directory.
  4. Using your IDE, open the relevant poexample workspace or solution file (poexamplevc6.dsw for VC++6 and poexamplevc7.sln for VC7.1++).
  5. The main part of the example is in the handle_po() function in the poexample.cpp file. (The code for this is shown below.) Scanning the comments should tell you what is happening at each stage.
  6. Compile, build and run.
  7. Observe the output gathered from the various parts of the input XML, the XML generated from modifying a copy of the C++ objects, and the XML generated from the original unmodified C++ objects.

Conclusion

The example demonstrates how easy it is to use LMX XML C++ data binding to interface C++ with XML. Although the example is short, it illustrates many of the key XML C++ data binding points, including how to unmarshal and marshal XML, as well as read and writing to different types of data.

poexample.cpp

The C++ file poexample.cpp is the file containing the main part of the example. It is reproduced here for your convenience.

    // Read in the po XML
    //-------------------
    
    // Declare somehwere to store any error code
    lmx::elmx_error l_error = lmx::ELMX_OK;
    
    // Construct an instance of c_root from the XML contained in the file po.xml.
    // (Code for c_root is generated by LMX)
    c_root l_po( "po.xml", &l_error );
    
    // If the XML was contained in memory, we could have done:
    //     c_root l_po( po_xml_buf, sizeof( po_xml_buf ), &l_error );

    if( l_error != lmx::ELMX_OK )
        {
        std::cout << "An error occurred while reading XML\n";
        return;
        }


    // 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() )
            {
            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";
            }

        const c_Items *lp_items = &lp_pot->get_items();
        
        float l_total_cost = 0;

        // Casts used to avoid vc7 warnings about converting size_t to unsigned int
        std::cout << "Number of items = " << static_cast<unsigned int>(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 )
            {
            // Casts used to avoid vc7 warnings about converting size_t to unsigned int
            std::cout << "Item " << static_cast<unsigned int>(l_i + 1) << ": " << 
                        static_cast<unsigned int>( 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
        //--------------------------------------
        std::cout << "\n\nModified XML output\n";

        if( l_alt_po.marshal( std::cout ) == lmx::ELMX_OK )
            std::cout << "Modified XML written successfully\n";
        else
            std::cout << "Error writing Modified XML\n";

        // Also marshal to a file
        if( l_alt_po.marshal( "po-out.xml" ) == lmx::ELMX_OK )
            std::cout << "Modified XML written successfully to file\n";
        else
            std::cout << "Error writing Modified XML to file\n";
        }


    // Write out the original XML
    //---------------------------
    std::cout << "\n\nXML output\n";

    if( l_po.marshal( std::cout ) == lmx::ELMX_OK )
        std::cout << "Original XML written successfully\n";
    else
        std::cout << "Error writing Original XML\n";
        

Generated Code Examples

The following is an extract of the function prototypes declared for the class c_item in po.h showing the facet and cardinality information applicable to each attribute and element. These additional comments make programming easier.

// Attribute(s)
//    partNum --> xs:string{/\d {3}-[A-Z]{2}/}
const lmx::tlmx_wide_string & get_partNum() const;
lmx::elmx_error set_partNum( const lmx::tlmx_wide_string & );

// Element(s)
//    productName --> xs:string
const lmx::tlmx_wide_string & get_productName() const;
lmx::elmx_error set_productName( const lmx::tlmx_wide_string & );

//    quantity --> xs:positiveInteger{1<=x<100}
lmx::tlmx_uns8 get_quantity() const;
lmx::elmx_error set_quantity( lmx::tlmx_uns8 );

//    USPrice --> xs:decimal
const lmx::tc_decimal & get_USPrice() const;
lmx::elmx_error set_USPrice( const lmx::tc_decimal & );

//    comment --> xs:string[0..1]
const lmx::tlmx_wide_string & get_comment() const;
lmx::elmx_error set_comment( const lmx::tlmx_wide_string & );
bool        isset_comment() const;
void        unset_comment();

//    shipDate --> xs:date[0..1]
const lmx::tc_date & get_shipDate() const;
lmx::elmx_error set_shipDate( const lmx::tc_date & );
bool        isset_shipDate() const;
void        unset_shipDate();

This is the corresponding generated HTML documentation showing the abbreviated type notation capturing the facets and cardinality of the various types. It is fully cross-linked making navigation around the generated code easy.

item

Class name:c_item
Kind:Element
Model:Sequence Complex Content
partNum --> xs:string{/\d{3}-[A-Z]{2}/}
    const lmx::tlmx_wide_string & get_partNum () const;
    lmx::elmx_error set_partNum ( const lmx::tlmx_wide_string & );
productName --> xs:string
    const lmx::tlmx_wide_string & get_productName () const;
    lmx::elmx_error set_productName ( const lmx::tlmx_wide_string & );
quantity --> xs:positiveInteger{1<=x<100}
    lmx::tlmx_uns8 get_quantity () const;
    lmx::elmx_error set_quantity ( lmx::tlmx_uns8 );
USPrice --> xs:decimal
    const lmx::tc_decimal & get_USPrice () const;
    lmx::elmx_error set_USPrice ( const lmx::tc_decimal & );
comment --> xs:string[0..1]
    const lmx::tlmx_wide_string & get_comment () const;
    lmx::elmx_error set_comment ( const lmx::tlmx_wide_string & );
    bool isset_comment() const;
    void unset_comment();
shipDate --> xs:date[0..1]
    const lmx::tc_date & get_shipDate () const;
    lmx::elmx_error set_shipDate ( const lmx::tc_date & );
    bool isset_shipDate() const;
    void unset_shipDate();
Common Functions
    bool is_occurs_ok() const;

This is a section of the generated unmarshal code from po.cpp. Notice the checks against the various facets, and the checks that the document is correctly formed. The use of ar_reader.handle_error allows you to selectively override errors, or call exceptions according to your preference.

*ap_event = ar_reader.tokenise( elem_event_map, *ap_name, lmx::EXNT_ELEM );
// ... intervening code removed ...
if( *ap_event == e_1_quantity )
    {
    bool l_end_of_element;
    if( ( ! ar_reader.skip_start_tag( &l_end_of_element ) || 
            l_end_of_element ) && (*ap_error = ar_reader.handle_error( lmx::ELMX_BAD_END_OF_START_TAG )) != lmx::ELMX_OK )
        return false;
    if( ar_reader.get_element_value( &l_value, lmx::EXWS_COLLAPSE ) )
        {
        if( ! ( lmx::is_valid_integer( l_value ) ) && 
                (*ap_error = ar_reader.handle_error( lmx::ELMX_VALUE_BAD_FORMAT )) != lmx::ELMX_OK )
            return false;
        if( ! ( l_value >= limit_22_min ) && 
                (*ap_error = ar_reader.handle_error( lmx::ELMX_VALUE_EXCEEDS_MIN )) != lmx::ELMX_OK )
            return false;
        if( ! ( l_value < limit_22_max ) && 
                (*ap_error = ar_reader.handle_error( lmx::ELMX_VALUE_EXCEEDS_MAX )) != lmx::ELMX_OK )
            return false;
        lmx::v_to_o( _quantity, l_value );
        present_quantity = true;
        }
    else if( (*ap_error = ar_reader.handle_error( lmx::ELMX_UNABLE_TO_READ_ELEMENT_VALUE )) != lmx::ELMX_OK )
        return false;

The following is a section of the generated marshal code from po.cpp. It has been designed to be simple and easy to understand.

lmx::elmx_error c_USAddress::marshal( lmx::c_xml_writer &ar_writer, const char *p_name ) const
{   
    ar_writer() << '<' << p_name;
    if( ! ar_writer.is_ns_attrs_written() )
        ar_writer.write_ns_attrs( ns_map, false );
    marshal_attributes( ar_writer );
    ar_writer(false) << '>' << ar_writer.endl();
    ar_writer.indent();
    lmx::elmx_error l_error;
    if( (l_error = marshal_child_elements( ar_writer )) != lmx::ELMX_OK )
        return l_error;
    ar_writer.undent();
    ar_writer() << "</" << p_name << '>' << ar_writer.endl();
    return lmx::ELMX_OK;
}

lmx::elmx_error c_USAddress::marshal_attributes( lmx::c_xml_writer &ar_writer ) const
{   
    if( present_country )
        ar_writer(false) << " country=\"" << lmx::as_xml( _country ) << '"';
    return lmx::ELMX_OK;
}

lmx::elmx_error c_USAddress::marshal_child_elements( lmx::c_xml_writer &ar_writer ) const
{   
    lmx::elmx_error l_error = lmx::ELMX_OK;
    ar_writer() << "<name>" << lmx::as_xml( _name ) << "</name>" << ar_writer.endl();
    ar_writer() << "<street>" << lmx::as_xml( _street ) << "</street>" << ar_writer.endl();
    ar_writer() << "<city>" << lmx::as_xml( _city ) << "</city>" << ar_writer.endl();
    ar_writer() << "<state>" << lmx::as_xml( _state ) << "</state>" << ar_writer.endl();
    ar_writer() << "<zip>" << lmx::as_xml( _zip ) << "</zip>" << ar_writer.endl();
    return lmx::ELMX_OK;
}


LMX XSD XML C++ Binding Overview  Overview  Download LMX XML C++ Data Binding  Download  Buy LMX XML C++ Data Binding  Buy  LMX Documentation XSD XML C++ Data Binding  Documentation  LMX C++ XML XSD Code Generator Quick Intro  Walkthrough  LMX C++ XML Data Binding Try On-line  Try On-line  LMX XML XSD C++ Binding Support  Support 
Copyright © 2003-2017, Codalogic Ltd. All Rights Reserved.