How to bind polymorphic types in gSOAP

What I'm trying to do using gSOAP:

  • Define data structures in an XML schema
  • Use wsdl2h and soapcpp2 to create C ++ classes that represent these structures.
  • Reading and writing these structures in XML from C ++

Please note that I am not using a web service at this point, I'm just interested in XML data binding.

If my classes look like this:

class Base {...}

class Der1: public Base {..}

Der2 class: public Base {...}

then I can serialize the base object (which may actually be one of the derived types) using:

std::ofstream myFile; myFile.open("output.out"); ctx.os = &myFile; Der1 obj; // or Der2 obj... // ... populate obj if (soap_write_Base(ctx, dynamic_cast<Base*>(&obj)) == SOAP_OK) { std::cout << "message serialized" << std::endl; } else { soap_print_fault(ctx, stderr); } 

and deserialize using:

 std::ifstream myFile; myFile.open("output.out"); ctx.is = &myFile; Der1 obj; if (soap_read_Der1(ctx, &obj) == SOAP_OK) { std::cout << "message deserialized" << std::endl; printMessage(msg); //debug } else { soap_print_fault(ctx, stderr); } 

where ctx is a pointer to a soap context declared as:

 soap* ctx = soap_new2(SOAP_XML_STRICT, SOAP_XML_INDENT); 

elsewhere in the code.

Can someone tell me how to change the deserialization code above to be able to read an object without knowing it if it is a Der1, Der2 or Base object?

Thanks!

+4
source share
1 answer

There are a few things you need to do.

First create your objects in C ++ so that they are obtained from xsd__anyType ( -p option on wsdl2h )

When you compile your code, define WITH_NO_IO and WITH_NO_HTTP so that you don't get gSoap calls by default and IO by default.

Then create a Serializer class, for example (pardon rant about XML):

 #pragma once #include <memory> namespace MyLib { class SerializerImpl; class xsd__anyType; class Serializer { std::shared_ptr<SerializerImpl> ser; public: Serializer(); Serializer(Serializer const &) = default; Serializer(Serializer &&o) : ser(std::forward<Serializer>(o).ser) { } ~Serializer() = default; Serializer &operator=(Serializer const& rhs) = default; Serializer &operator=(Serializer && rhs) { ser = std::forward<Serializer>(rhs).ser; return *this; } // Serialize 'value' into 'out'. void Serialize(xsd__anyType const &value, std::ostream &out); // Returns a shared pointer to the xsd_anyType that comes in 'in'. // The shared_ptr returned contains a custom deleter because gSoap ties // allocated values to the gSoap context used to deserialize it in the // first place. I think that was an attempt at adding some kind of // garbage collection so one didn't have to worry about deleting it except, // of course, that fails if you get rid of the context prior to the // end of the life of the deserialized value. Nobody does XML C++ bindings // right. XML sucks and C++ doesn't and it is hard to translate suck to // non-suck. std::shared_ptr<xsd__anyType> Deserialize(std::istream &in); }; } 

The implementation looks like this:

 #include "MyLibH.h" #include "stdsoap2.h" #include "Serializer.h" namespace MyLib { static int fsend(struct soap* ctx, char const *buf, size_t len) { if (!ctx->os) { throw std::logic_error("soap.fsend the struct soap 'os' member must be set."); } ctx->os->write(buf, len); return SOAP_OK; } static size_t frecv(struct soap* ctx, char* buf, size_t len) { if (!ctx->is) { throw std::logic_error("soap.fsend the struct soap 'is' member must be set."); } ctx->is->read(buf, len); return ctx->is->gcount(); } static SOAP_SOCKET fopen(struct soap*, const char*, const char*, int) { throw std::logic_error("soap.fopen not implemented for Serializer."); } static int fclose(struct soap *ctx) { return SOAP_OK; } static int fclosesocket(struct soap*, SOAP_SOCKET) { throw std::logic_error("soap.fclosesocket not implemented for Serializer."); } static int fshutdownsocket(struct soap*, SOAP_SOCKET, int) { throw std::logic_error("soap.fshutdownsocket not implemented for Serializer."); } static SOAP_SOCKET faccept(struct soap*, SOAP_SOCKET, struct sockaddr*, int *n) { throw std::logic_error("soap.faccept not implemented for Serializer."); } class SerializerImpl { struct soap mutable soap; public: SerializerImpl(); ~SerializerImpl(); struct soap *ctx() const { return &soap; } }; SerializerImpl::SerializerImpl() { soap_init(&soap); // compiled with WITH_NOIO so set these function pointers soap.fsend = fsend; soap.frecv = frecv; soap.fopen = fopen; soap.fclose = fclose; soap.fclosesocket = fclosesocket; soap.fshutdownsocket = fshutdownsocket; soap.fpoll = nullptr; soap.faccept = faccept; // Set input/output mode soap_imode(&soap, SOAP_ENC_XML); soap_set_omode(&soap, SOAP_XML_INDENT); } SerializerImpl::~SerializerImpl() { // remove deserialized class instances (C++ objects) soap_destroy(&soap); // clean up and remove deserialized data soap_end(&soap); // detach context (last use and no longer in scope) soap_done(&soap); } Serializer::Serializer() : ser(std::make_shared<SerializerImpl>()) { } void Serializer::Serialize(xsd__anyType const& value, std::ostream &out) { soap_begin(ser->ctx()); ser->ctx()->is = &in; soap_free_temp(ser->ctx()); int type; int err; char errbuf[200]; if ((err = soap_begin_recv(ser->ctx())) != SOAP_OK) { _snprintf_s( errbuf, sizeof(errbuf), _TRUNCATE, "Serializer::Deserialize failed soap_begin_recv: %d", err); errbuf[sizeof(errbuf) - 1] = 0; throw std::exception(errbuf); } // Create a deleter for the element returned from 'soap_getelement()' auto serializerImpl = this->ser; auto deleteElement = [serializerImpl](void *toDelete) { soap_dealloc(serializerImpl->ctx(), toDelete); }; // parse the XML into an element std::unique_ptr<void, decltype(deleteElement)> res(soap_getelement(ser->ctx(), &type), deleteElement); if (!res) { // populate ser->ctx()->msgbuf with more detailed information soap_set_fault(ser->ctx()); if (ser->ctx()->msgbuf) { _snprintf_s( errbuf, sizeof(errbuf), _TRUNCATE, "Serializer::Deserialize failed soap_getelement: %s", ser->ctx()->msgbuf); } else { _snprintf_s( errbuf, sizeof(errbuf), _TRUNCATE, "Serializer::Deserialize failed soap_getelement: %d", ser->ctx()->error); } errbuf[sizeof(errbuf) - 1] = 0; throw std::exception(errbuf); } if ((err = soap_end_recv(ser->ctx())) != SOAP_OK) { _snprintf_s( errbuf, sizeof(errbuf), _TRUNCATE, "Serializer::Deserialize failed soap_end_recv: %d", err); errbuf[sizeof(errbuf) - 1] = 0; throw std::exception(errbuf); } // anything that can be cast as an xml_Any gets cast here switch (type) { case SOAP_TYPE_MyLib_ns1__FooType: case SOAP_TYPE_MyLib_ns1__BarType: case SOAP_TYPE_MyLib_ns1__BazType: // In theory, res is a subclass of xsd_anyType, so cast // it here as if it was auto anyType = static_cast<xsd__anyType *>(res.release()); // build a shared pointer with the custom deleter that keeps serializerImpl auto ret = std::shared_ptr<xsd__anyType>(anyType, deleteElement); return ret; } _snprintf_s( errbuf, sizeof(errbuf), _TRUNCATE, "Serializer::Deserialize failed - " "unsupported cast of type %d to xsd__anyType", type); errbuf[sizeof(errbuf) - 1] = 0; throw std::exception(errbuf); } } 

With this class you can create a Serializer ser; , then do ser.Serialize(myEntity, myOutputStream); or auto myEntity = ser.Deserialize(myInputStream); .

You can see the secret of polymorphic deserialization in the Deserialize() method, where it calls soap_getelement() , which returns a void pointer for any type that it can deserialize. Then, if the type is the one that is known to be based on xsd__anyType , then it is converted to shared_ptr<xsd__anyType> using the special delete method that is contained in the context of struct soap , so it can delete gSoap properly, The ability to distinguish xsd__anyType is this is the reason why we told wsdl2h to deduce all our types from this type using the -p option.

Note that in order to make this work for me, I had to create some other functions. The way I built the source with wsdl2h and soapcpp2 , WITH_NOGLOBAL , was defined. This caused some undefined functions. I made the definitions as follows:

 #include "MyLib3.nsmap" SOAP_FMAC3 const char ** SOAP_FMAC4 soap_faultcode(struct soap *soap) { static char const *ret; ret = nullptr; return &ret; } SOAP_FMAC3 const char ** SOAP_FMAC4 soap_faultsubcode(struct soap *soap) { static char const *ret; ret = nullptr; return &ret; } SOAP_FMAC3 const char ** SOAP_FMAC4 soap_faultstring(struct soap *soap) { static char const *ret; ret = nullptr; return &ret; } SOAP_FMAC3 const char ** SOAP_FMAC4 soap_faultdetail(struct soap *soap) { static char const *ret; ret = nullptr; return &ret; } SOAP_FMAC3 const char * SOAP_FMAC4 soap_check_faultsubcode(struct soap *soap) { return nullptr; } SOAP_FMAC3 const char * SOAP_FMAC4 soap_check_faultdetail(struct soap *soap) { return nullptr; } SOAP_FMAC3 void SOAP_FMAC4 soap_serializefault(struct soap *soap) { } SOAP_FMAC3 void SOAP_FMAC4 soap_serializeheader(struct soap *soap) { } SOAP_FMAC3 int SOAP_FMAC4 soap_putheader(struct soap *soap) { return SOAP_OK; } SOAP_FMAC3 int SOAP_FMAC4 soap_getfault(struct soap *soap) { return SOAP_OK; } SOAP_FMAC3 int SOAP_FMAC4 soap_putfault(struct soap *soap) { return SOAP_OK; } SOAP_FMAC3 int SOAP_FMAC4 soap_getheader(struct soap *soap) { return SOAP_OK; } 
0
source

All Articles