【问题标题】:How to bind polymorphic types in gSOAP如何在 gSOAP 中绑定多态类型
【发布时间】:2012-11-29 02:53:33
【问题描述】:

我正在尝试做的事情,利用 gSOAP:

  1. 在 XML 模式中定义数据结构
  2. 使用 wsdl2h 和 soapcpp2 生成代表这些结构的 C++ 类
  3. 从 C++ 以 XML 格式读取和写入这些结构

请注意,此时我没有使用 Web 服务,我只是对 XML 数据绑定感兴趣。

如果我的课程看起来像:

类基{ ... }

类 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上下文的指针,声明为:

soap* ctx = soap_new2(SOAP_XML_STRICT, SOAP_XML_INDENT);

代码中的其他地方。

谁能告诉我如何更改上面的反序列化代码,以便能够在不预先知道它是 Der1、Der2 还是 Base 对象的情况下读取对象?

谢谢!

【问题讨论】:

    标签: c++ xml gsoap


    【解决方案1】:

    你需要做几件事。

    首先,构建您的 C++ 对象,以便它们派生自 xsd__anyTypewsdl2h 上的 -p 选项)

    在编译代码时,定义 WITH_NO_IOWITH_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 = &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);
        }
    }
    

    使用这个类,您可以创建一个Serializer ser;,然后执行ser.Serialize(myEntity, myOutputStream);auto myEntity = ser.Deserialize(myInputStream);

    您可以在Deserialize() 方法中看到多态反序列化的秘密,该方法调用soap_getelement(),该方法为它可以反序列化的任何类型返回一个空指针。然后,如果该类型是已知基于xsd__anyType 的类型,那么它将使用自定义删除方法强制转换为shared_ptr&lt;xsd__anyType&gt;,该方法保留struct soap 上下文,因此它可以以适当的gSoap 方式删除.转换为 xsd__anyType 的能力是我们告诉 wsdl2h 使用 -p 选项从该类型派生所有类型的原因。

    请注意,要让它为我工作,我必须创建一些其他功能。 使用wsdl2hsoapcpp2WITH_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;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-11
      • 1970-01-01
      • 2016-01-08
      相关资源
      最近更新 更多