Encoding with C XSD Code

Previous Menu Next

Encoding with C Code Generated from XML Schemas

Encoding is similar to decoding. Let's look at how you would do the encoding to create the instance that we looked at earlier:

   <purchase>
     <customer number="12345">
       John Smith
     </customer>
     <store>
       Objective Systems Online Store
     </store>
     <item>
       XBinder XML Data Binding Tool
     </item>
     <price>
       750.00
     </price>
   </purchase>
   
		

You first have to include the generated header file that defines the structures that correspond to the XML elements:

  #include "Purchase.h"

Next you have to declare an OSCTXT structure, a structure for the global element (PurchaseRecord), and a structure for the items that can repeat:

   PurchaseRecord* pPurchase;
   PurchaseRecord_3 itemAndPrice1;
   OSCTXT       ctxt;

Notice that the PurchaseRecord is declared as a pointer to the structure, while the potentially repeating items are declared as an instance on the stack. There is no special reason for doing them this way as opposed to another way, we're just illustrating the different ways these declarations can be done.

Next you can define static strings for the text data that needs to be set into the structures:

   static const char*  customerName = "John Smith";
   static const char*  itemName1 = "XBinder XML Data Binding Tool";
   static const char*  storeName = "Objective Systems Online Store";

Remember this is straight C, not C++, so all declarations must appear before executable code.

Next you need to initialize the context structure:

   stat = rtXmlInitContext(&ctxt);

Next you need to allocate and initialize your PurchaseRecord structure:

   pPurchase = rtxMemAllocType(&ctxt, PurchaseRecord);
   Init_PurchaseRecord(&ctxt, pPurchase);

The rtxMemAllocType() call is using a macro that's defined in the XBinder C run-time library. The Init_PurchaseRecord() function is a generated function.

Note that had we allocated the PurchaseRecord on the stack (e.g., with PurchaseRecord purchase; instead of PurchaseRecord* pPurchase), the call to rtxMemAllocType() would not have been necessary, but the call to Init_PurchaseRecord() would still have been necessary (as Init_PurchaseRecord(&ctxt, &purchase);).

Now you can set the customer number, the customer name, and the store name:

   pPurchase->customer.number = 12345;
   pPurchase->customer.m.numberPresent = 1;
   pPurchase->customer._base.value = customerName;
   pPurchase->store.value = storeName;

Now you need to set up the structure that holds the items that might repeat. You declared a structure of type PurchaseRecord_3 called itemAndPrice1 at the beginning. You must now initialize that structure:

   Init_PurchaseRecord_3(&ctxt, &itemAndPrice1);

Like Init_PurchaseRecord(), Init_PurchaseRecord_3() is a generated function.

You can now set the values associated with this structure:

   itemAndPrice1.item.value = itemName1;
   itemAndPrice1.price = 750.00;

The next step is to put the populated item and price structure into the linked list that's generated to handle the sequence that might repeat:

   rtxDListAppend(&ctxt, &(pPurchase->_seq3), &itemAndPrice1);

The rtxDListAppend() function is in the XBinder C run-time library. You're basically telling it to take the item and price structure and append it to the linked list generated into the PurchaseRecord structure.

Suppose this customer also bought a bicycle for $100.00 in this same transaction. You could easily add that information into the structures:

   PurchaseRecord_3 itemAndPrice2;
   .
   .
   .
   static const char* itemName2 = "Bicycle";
   .
   .
   .
   Init_PurchaseRecord_3(&ctxt, &itemAndPrice2);
   itemAndPrice2.item.value = itemName2;
   itemAndPrice2.price = 100.00;
   rtxDListAppend(&ctxt, &(pPurchase->_seq3), &itemAndPrice2);

For this example, to keep things simple, you can tell the encoding mechanism to do its own dynamic memory allocation as needed:

   stat = rtXmlSetEncBufPtr (&ctxt, 0, 0);

And now the actual encoding can be done:

   stat = XmlE_purchase (&ctxt, pPurchase);

The XmlE_purchase() function is a generated function to handle the encoding of a PurchaseRecord instance.

Once encoding is complete, you can use the rtXmlGetEncBufPtr() function to get a pointer to the beginning of the encoded instance:

   const OSOCTET* pMsgBuf;
   .
   .
   .
   pMsgBuf = rtXmlGetEncBufPtr (&ctxt);

This pointer can be cast to (char *).

You can also write the encoded message to a file:

   stat = rtXmlWriteToFile (&ctxt, "message_out.xml");

And before leaving, you should use the rtxFreeContext() function to release all memory associated with the context structure that you've been using:

   rtxFreeContext (&ctxt);

So putting all of this together, you have something like this:

   #include "Purchase.h"
   .
   .
   .
   PurchaseRecord* pPurchase;
   PurchaseRecord_3 itemAndPrice1;
   OSCTXT       ctxt;
   const OSOCTET* pMsgBuf;
   static const char*  customerName = "John Smith";
   static const char*  itemName1 = "XBinder XML Data Binding Tool";
   static const char*  storeName = "Objective Systems Online Store";
   .
   .
   .
   stat = rtXmlInitContext (&ctxt);
   pPurchase = rtxMemAllocType(&ctxt, PurchaseRecord);
   Init_PurchaseRecord(&ctxt, pPurchase);
   pPurchase->customer.number = 12345;
   pPurchase->customer.m.numberPresent = 1;
   pPurchase->customer._base.value = customerName;

   pPurchase->store.value = storeName;
   Init_PurchaseRecord_3(&ctxt, &itemAndPrice1);
   itemAndPrice1.item.value = itemName1;
   itemAndPrice1.price = 750.00;
   rtxDListAppend(&ctxt, &(pPurchase->_seq3), &itemAndPrice1);
   stat = rtXmlSetEncBufPtr (&ctxt, 0, 0);
   stat = XmlE_purchase (&ctxt, pPurchase);
   pMsgBuf = rtXmlGetEncBufPtr (&ctxt);
   stat = rtXmlWriteToFile (&ctxt, "message_out.xml");
   rtxFreeContext (&ctxt);

To compile this code, you need to specify the XBinder install directory as a directory to search for included .h files. For example, if you installed XBinder into the folder c:\xbv260 on a Windows system, then you would specify c:\xbv260 as a directory to search for included .h files during the compile.

To link the code you need to specify a couple of libraries whose specific names and locations will vary depending on what compiler you're using and what version of XBinder you have. For example, for XBinder v2.6 on Windows, the libraries in the c\lib hierarchy are built with Visual Studio 2015. In newer versions of XBinder these libraries might be built with a different tool, most likely a newer version of Visual Studio. See the table below for some examples for v2.6 on Windows:

CompilerLibraries
Microsoft Visual Studio 2015c\lib\osysrt_a.lib
c\lib\osysrtxml.lib or osysrtxml_a.lib
Microsoft Visual Studio 2017c_vs2017\lib\osysrt_a.lib
c_vs2017\lib\osysrtxml.lib or osysrtxml_a.lib
gccc_gnu/libosysrt.a
c_gnu/libosysrtxml.a

 

In each case in the table above the indicated directory structure lives underneath the XBinder install directory.

Previous Menu Next