Что я пытаюсь сделать, используя gSOAP:
Обратите внимание, что на данный момент я не использую веб-сервис, я просто заинтересован в привязке данных XML.
Если мои занятия выглядят так:
класс Base {
…
}
класс Der1: публичная база {
..
}
класс Der2: публичная база {
…
}
затем я могу сериализовать объект Base (который может быть одним из производных типов), используя:
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);
}
и десериализовать, используя:
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);
}
где ctx — указатель на мыльный контекст, объявленный как:
soap* ctx = soap_new2(SOAP_XML_STRICT, SOAP_XML_INDENT);
в другом месте кода.
Может кто-нибудь сказать мне, как изменить код десериализации выше, чтобы иметь возможность читать объект, не зная заранее, если это объект Der1, Der2 или Base?
Спасибо!
Есть пара вещей, которые вам нужно сделать.
Во-первых, создайте ваши объекты C ++, чтобы они происходили от xsd__anyType
( -p
вариант на wsdl2h
)
Когда вы компилируете свой код, определите WITH_NO_IO
а также WITH_NO_HTTP
так что вы не получаете по умолчанию gSoap HTTP и IO вызовы.
Затем создайте Serializer
класс вроде (простите за разгул 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);
};
}
Реализация выглядит так:
#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 = ∈
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);
}
}
С помощью этого класса вы можете создать Serializer ser;
тогда делай ser.Serialize(myEntity, myOutputStream);
или же auto myEntity = ser.Deserialize(myInputStream);
,
Вы можете увидеть секрет полиморфной десериализации в Deserialize()
метод, где это вызывает soap_getelement()
который возвращает пустой указатель для любого типа, который он может десериализовать. Затем, если тип тот, который, как известно, основан на xsd__anyType
затем он попадает в shared_ptr<xsd__anyType>
с пользовательским методом удаления, который держит на struct soap
контекст, чтобы он мог удалить надлежащим образом gSoap. Способность к xsd__anyType
это причина, по которой мы сказали wsdl2h
вывести все наши типы из этого типа с -p
вариант.
Обратите внимание, чтобы это сработало, мне нужно было создать некоторые другие функции. Путь я построил источник с wsdl2h
а также soapcpp2
, WITH_NOGLOBAL
определился. Это вызвало некоторые неопределенные функции. Я сделал определения следующим образом:
#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;
}
Других решений пока нет …