Search code examples
qtwrappergsoap

Send qt object with gsoap


I want to send qt objects like qlist or qstring or qimage or qbytearray ... with gsoap? Base on my science about gsoap we can only send data with primitive type like char * or int ... . Example : In client I have an struct like this

Struct mystr
{
 QString x;
 QImage y;
QbyteArray z;
...
}
QList<mystr> mylist;

I fill this list with 100000 data structure and I want send this to server. How can do this?


Solution

  • If it helps, the recent release of the gsoap toolkit 2.8.34 now supports serialization of QT primitive types and QT containers in XML with minimal effort.

    QT types are bound to XSD types by name, basically just a typedef. These types are then serialized using a custom serializer that is compiled and linked with your code.

    Perhaps it is easier to understand all off this with a simple example.

    To use QString as a serializable type, simply add an #import "custom/qstring.h" to your header file that has the data binding interface for soapcpp2. Then run soapcpp2 on the file and compile the generated soapC.cpp with stdsoap2.cpp. Don't forget to #include "soapH.h" (which also includes soapStub.h).

    Here is an example header file for soapcpp2 with the types that you want to serialize in XML by declaring them as data bindings and importing the custom QT types that you want:

    ////////////////////////////////////////////////////////////////////////////
    //
    // Import the QT types that we want to bind to XSD types xsd__Type
    //
    ////////////////////////////////////////////////////////////////////////////
    
    #import "custom/qstring.h"           // typedef QString xsd__string
    #import "custom/qbytearray_base64.h" // typedef QByteArray xsd__base64Binary
    
    ////////////////////////////////////////////////////////////////////////////
    //
    // Declare QT container template(s) we will use
    //
    ////////////////////////////////////////////////////////////////////////////
    
    template <class T> class QList;
    
    ////////////////////////////////////////////////////////////////////////////
    //
    // Define an XML namespace "ns" for our schema
    //
    ////////////////////////////////////////////////////////////////////////////
    
    //gsoap ns schema namespace: urn:MyTypes
    
    ////////////////////////////////////////////////////////////////////////////
    //
    // Define C++ types that use the xsd__Type QT types imported above
    //
    ////////////////////////////////////////////////////////////////////////////
    
    class ns:MyStruct
    {
     public:
      xsd__string x;       // a QString object
      xsd__base64Binary y; // a QByteArray object
    };
    class ns:MyData
    {
     public:
      QList<ns:MyStruct> z; // a QT list of MyStruct
    };
    

    Note that I've kept it simple by using ns: as a prefix rather than ns__ (ns with double underscores). The difference is that ns: will not be part of your C++ type names, whereas ns__ will be part of your C++ type names. This is a gsoap convention.

    After running soapcpp2 on this header file, it generates a schema ns.xsd with your XML types:

    <schema targetNamespace="urn:MyTypes"
      xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
      xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:xsd="http://www.w3.org/2001/XMLSchema"
      xmlns:ns="urn:MyTypes"
      xmlns="http://www.w3.org/2001/XMLSchema"
      elementFormDefault="unqualified"
      attributeFormDefault="unqualified">
      <import namespace="http://schemas.xmlsoap.org/soap/encoding/"/>
      <complexType name="MyStruct"><!-- ns:MyStruct -->
          <sequence>
            <element name="x" type="xsd:string" minOccurs="1" maxOccurs="1"/><!-- ns:MyStruct::x -->
            <element name="y" type="xsd:base64Binary" minOccurs="1" maxOccurs="1"/><!-- ns:MyStruct::y -->
          </sequence>
      </complexType>
      <complexType name="MyData"><!-- ns:MyData -->
          <sequence>
            <element name="z" type="ns:MyStruct" minOccurs="0" maxOccurs="unbounded"/><!-- ns:MyData::z -->
          </sequence>
      </complexType>
    </schema>
    

    You can get rid of the SOAP stuff with option -0 (dash zero) for soapcpp2.

    If you want to define a root element for MyData in this schema, then add the following to the header file and rerun soapcpp2 on this file:

    typedef ns:MyData _ns__myRoot; // ns:myRoot is an XML element of type ns:MyData
    

    The XML serializer can be used as follows:

    #include "soapH.h"   // this is generated by soapcpp2
    #include "ns.nsmap"  // this is generated by soapcpp2
    ...
    soap *ctx = soap_new1(SOAP_XML_INDENT); // create a context
    MyData data;
    ...
    data.push_back(MyStruct());    // populate some data
    ctx->os = &std::cout;
    soap_write__ns__myRoot(ctx, &data); // serialize data in XML
    ...
    ctx->is = &std::cin;
    soap_read__ns__myRoot(ctx, &data);  // parse data from XML
    ...
    soap_destroy(ctx);
    soap_end(ctx);
    soap_free(ctx).
    

    Hope this helps.