Decoding with C XSD Code

Previous Menu Next

Decoding with C Code Generated from XML Schemas

Let's first look at how you would decode an instance that conforms to the schema in the Purchase.xsd file that we've been using, which is reproduced here:

   <xsd:element name="purchase" type="PurchaseRecord"/>

   <xsd:complexType name="PurchaseRecord">
      <xsd:sequence>
         <xsd:element name="customer" type="CustomerType" maxOccurs="1"/>
         <xsd:element name="store" type="xsd:string" maxOccurs="1"/>
         <xsd:sequence maxOccurs="unbounded">
            <xsd:element name="item" type="xsd:string"/>
            <xsd:element name="price" type="xsd:float"/>
         </xsd:sequence>
      </xsd:sequence>
   </xsd:complexType>

   <xsd:complexType name="CustomerType">
      <xsd:simpleContent>
         <xsd:extension base="xsd:string">
            <xsd:attribute name="number" type="xsd:integer"/>
         </xsd:extensiion>
      </xsd:simpleContent>
   </xsd:complexType>
   

Your first step is to initialize a structure that holds context information relating to the XBinder operations you're trying to do. This structure is simply referred to as a context structure, and it has a type of OSCTXT. The definition for this type, as is the case for almost all of the definitions that you will need when using XBinder, can be defined by including the .h file generated for your .xsd file. In this case:

  #include "Purchase.h"

You then need to declare a variable of type OSCTXT:

   OSCTXT ctxt;

Of course you can also declare a pointer to OSCTXT and then allocate the memory for it yourself:

   OSCTXT* pctxt;
   .
   .
   .
   pctxt = (OSCTXT*) malloc(sizeof(OSCTXT));

If you take this second approach, however, you will need to remember to call the free() function when you're done with the context to release the memory; otherwise, you may create a memory leak. The remainder of this section will assume that you have taken the first approach.

Your next step is to call a function called rtXmlInitContext(). This function, as its name implies, initializes a context structure and takes a pointer to a context structure as its argument. The function lives in the XBinder C run-time library, which comes with the XBinder product. We will cover how to link against the library that contains this function in a little bit.

   stat = rtXmlInitContext(&ctxt);

The variable that the function returns is an "int" variable that indicates whether the function worked. A value of zero indicates success.

You then need to call a generated function that initializes the structure that corresponds to the global element defined in your .xsd file. This global element is sometimes referred to in the generated code as a PDU (Protocol Data Unit). This term is borrowed from ASN.1 and refers to the top-level element that a .xsd file defines; i.e., the element that contains all of the data.

In this case the name of the generated function is Init_PurchaseRecord(). It takes two arguments: a pointer to a context structure (OSCTXT*), and a pointer to the structure that defines the top-level element.

This top-level element structure in your case is called PurchaseRecord, since the Purchase.xsd file specifies PurchaseRecord as the data type for the top-level element. So what you need to do is declare a variable of type PurchaseRecord, and then specify the address of this variable in the call to Init_PurchaseRecord():

   PurchaseRecord pdu;
   .
   .
   .
   Init_PurchaseRecord(&ctxt, &pdu);

Remember that you've already declared and initialized the context structure ctxt.

You next need to call a run-time library function called rtxStreamFileCreateReader() to associate the file that contains your XML instance with the context structure. To use this function you must include a file called rtxStreamFile.h, which lives in the rtxsrc directory in the XBinder installation. We'll look at how to compile so that this file is properly included in a little bit. The directive to include this file would look like this:

  #include "rtxsrc/rtxStreamFile.h"

Then the call to the function would look like this:

   stat = rtxStreamFileCreateReader (&ctxt, xmlfname);

The "xmlfname" variable is simply a char* variable that contains the name of the file that contains the XML to be decoded.

With this done you're ready to do the actual decode. The decode is accomplished by calling another generated function called XmlD_purchase(). This function takes as arguments a pointer to a context structure and a pointer to a top-level definition structure, or a pdu structure. The call would look like this:

  stat = XmlD_purchase (&ctxt, &pdu);

What this call is basically saying is that we want to decode the XML instance that's available in the data stream associated with the context variable ctxt (the association you made in the call to rtxStreamFileCreateReader()) and place the decoded data into the top-level element structure defined by the variable "pdu" (which in your case is of type PurchaseRecord).

Once the decoding is done and before the program that does it terminates there is some housekeeping that should be done:

   rtxErrReset (&ctxt);
   rtxStreamClose (&ctxt);
   rtxFreeContext (&ctxt);

So putting this all together, you'd have something like this:

   #include <Purchase.h>
   #include <rtxsrc/rtxStreamFile.h>
   .
   .
   .
   OSCTXT ctxt;
   PurchaseRecord pdu;
   int stat;
   char* xmlfname = "Purchase.xml";
   .
   .
   .
   stat = rtXmlInitContext(&ctxt);
   Init_PurchaseRecord(&ctxt, &pdu);
   stat = rtxStreamFileCreateReader (&ctxt, xmlfname);
   stat = XmlD_purchase (&ctxt, &pdu);
   rtxErrReset (&ctxt);
   rtxStreamClose (&ctxt);
   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