2012-12-11 19 views
4

我所試圖做的,利用gSOAP的:如何gSOAP的結合態的類型

  1. 定義的數據結構中的XML模式
  2. 使用wsdl2h和soapcpp2生成C++類的代表,這些結構
  3. 從C++讀取和寫入XML中的這些結構

請注意,此時我沒有使用Web服務,我只是對XML數據綁定感興趣。

如果我的課是這樣的:

類基地{ ... }

類Der1:公共基礎{ .. }

類DER2:公共基礎{ 。 ... }

然後我可以序列化一個基本對象(實際上可能是派生類型之一)使用:

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對象?

謝謝!

回答

0

有幾件事情你需要做。

首先,建立你的C++對象,所以他們從xsd__anyType獲得(在-p選項上wsdl2h

當您編譯代碼,定義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);或。

您可以在Deserialize()方法中看到多態反序列化的祕密,它會調用soap_getelement(),它會爲任何可以反序列化的類型返回一個void指針。然後,如果該類型是已知基於xsd__anyType的類型,則它將被轉換爲shared_ptr<xsd__anyType>,並使用自定義刪除方法保存到struct soap上下文中,以便可以使用適當的gSoap方式進行刪除。能夠投射到xsd__anyType是我們告知wsdl2h-p選項中衍生出所有類型的原因。

請注意,爲了讓它適用於我,我必須創建一些其他功能。方式內置源wsdl2hsoapcpp2,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; 
}