2012-02-08 26 views
6

我一直在玩gSOAP XML數據綁定,將XML文檔加載到C++類中,修改數據並將其序列化回XML。如果從文件流中讀取數據,gSOAP爲什麼將stdin模式設置爲二進制模式?

這裏是XML的片段 - library.xml:

<?xml version="1.0" encoding="UTF-8"?>  
<gt:Library 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:gt="http://www.bk.com/gSOAP/test"> 
    <gt:Books> 
     <gt:Book isbn="0132350882" author="Robert C. Martin" title="Clean Code"> 
      <gt:CopiesAvailable>2</gt:CopiesAvailable> 
     </gt:Book> 
     <gt:Book isbn="020161622X" author="Andrew Hunt" title="The Pragmatic Programmer"> 
      <gt:CopiesAvailable>0</gt:CopiesAvailable> 
     </gt:Book> 
     <gt:Book isbn="0201633612" author="Erich Gamma" title="Design patterns"> 
      <gt:CopiesAvailable>1</gt:CopiesAvailable> 
     </gt:Book>  
    </gt:Books> 
    ... 
</gt:Library> 

下面的代碼加載XML爲對象,修改對象和序列化回XML。 請注意,XML是通過文件流從文件加載的,並且要添加的數據是通過stdin(cin)從用戶獲得的。

main.cpp中:

#include "soapH.h" 
#include "gt.nsmap" 
#include <iostream> 
#include <fstream> 
#include <string> 
#include <sstream> 
using std::cin; 
using std::cout; 
using std::endl; 
using std::ifstream; 
using std::ofstream; 
using std::fstream; 
using std::string; 
using std::stringstream; 

void DisplayAllBooks(const _gt__Library& library) 
{ 
    cout << "\n\nDisplaying all books in the library:" << endl; 

    std::vector<_gt__Library_Books_Book>::const_iterator it = library.Books.Book.begin(); 
    for(;it != library.Books.Book.end(); it++) 
    { 
     cout << "\nBook:\n" << "\tTitle:" << (*it).title << "\n\tAuthor:" << (*it).author <<"\n\tISBN: " << (*it).isbn << "\n\tCopies available: " << static_cast<int>((*it).CopiesAvailable) << endl; 
    } 
} 

void AddBook(_gt__Library& library) 
{ 
    cout << "\n\nAdding a new book:" << endl; 

    _gt__Library_Books_Book book; 

    cout << "\tTitle: " << std::flush; 
    getline(cin, book.title); 

    cout << "\tAuthor: " << std::flush; 
    getline(cin, book.author); 

    cout << "\tISBN:" << std::flush; 
    getline(cin, book.isbn); 

    cout << "\tCopies available: " << std::flush; 
    string strCopiesAvailable; 
    getline(cin, strCopiesAvailable); 
    stringstream ss(strCopiesAvailable); 
    ss >> book.CopiesAvailable; 

    library.Books.Book.push_back(book); 
} 

// Terminate and destroy soap 
void DestroySoap(struct soap* pSoap) 
{ 
    // remove deserialized class instances (C++ objects) 
    soap_destroy(pSoap); 

    // clean up and remove deserialized data 
    soap_end(pSoap); 

    // detach context (last use and no longer in scope) 
    soap_done(pSoap); 
} 

int main() 
{ 

    // 
    // Create and intialize soap 
    // 

    // gSOAP runtime context 
    struct soap soap; 

    // initialize runtime context 
    soap_init(&soap); 

    // Set input mode 
    soap_imode(&soap, SOAP_ENC_XML); 

    // reset deserializers; start new (de)serialization phase 
    soap_begin(&soap); 

    // 
    // Load XML (Deserialize) 
    // 

    _gt__Library library; 
    string strXML = "library.xml"; 

    ifstream fstreamIN(strXML); 
    soap.is = &fstreamIN;        

    // calls soap_begin_recv, soap_get__gt__Library and soap_end_recv 
    if(soap_read__gt__Library(&soap, &library) != SOAP_OK) 
    { 
     std::cout << "soap_read__gt__Library() failed" << std::endl; 
     DestroySoap(&soap); 
     return 1; 
    } 

    fstreamIN.close(); 

    // 
    // Display books before and after adding a new book 
    // 

    DisplayAllBooks(library); 
    AddBook(library); 
    DisplayAllBooks(library); 

    // 
    // Serialize 
    // 

    soap_set_omode(&soap, SOAP_XML_INDENT); 

    ofstream fstreamOUT("library.xml"); 
    soap.os = &fstreamOUT; 

    // calls soap_begin_send, soap_serialize, soap_put and soap_end_send 
    if(soap_write__gt__Library(&soap, &library) != SOAP_OK) 
    { 
     std::cout << "soap_write__gt__Library() failed" << std::endl; 
     DestroySoap(&soap); 
     return 1; 
    } 

    fstreamOUT.close(); 

    DestroySoap(&soap); 

    return 0; 
} 

運行這個測試應用程序後,一切都從所有新加入的元素除了罰款已經與一個回車符(CR - &#xD;)終止字符串:

修改XML是這樣的:

<?xml version="1.0" encoding="UTF-8"?> 
<gt:Library 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:gt="http://www.bk.com/gSOAP/test"> 
    <gt:Books> 
     <gt:Book isbn="0132350882" author="Robert C. Martin" title="Clean Code"> 
      <gt:CopiesAvailable>2</gt:CopiesAvailable> 
     </gt:Book> 
     <gt:Book isbn="020161622X" author="Andrew Hunt" title="The Pragmatic Programmer"> 
      <gt:CopiesAvailable>0</gt:CopiesAvailable> 
     </gt:Book> 
     <gt:Book isbn="0201633612" author="Erich Gamma" title="Design patterns"> 
      <gt:CopiesAvailable>1</gt:CopiesAvailable> 
     </gt:Book> 
     <gt:Book isbn="12345678&#xD;" author="Scott Meyers&#xD;" title="Effective C++&#xD;"> 
      <gt:CopiesAvailable>123</gt:CopiesAvailable> 
     </gt:Book> 
    </gt:Books> 
    ... 
</gt:Library> 

我追查錯誤的來源,發現如下:

soap_read__gt__Library()調用soap_begin_send()其執行以下行:

_setmode(soap->recvfd, _O_BINARY); 

soap->recvfd被設置爲在0soap_init()0stdin文件描述符的值。

一旦stdin的模式改變爲二進制,STL庫不分析\r\n到一個\n進行讀操作和getline(cin, str),像往常一樣,一切都讀取高達\n,複製\r爲輸出字符串。這正是回車符,它出現在最終XML中的新字符串中。

我的問題是:如果數據源是文件流,爲什麼gSOAP會修改stdin模式?這是gSOAP中的錯誤嗎?

注:

正如預期的那樣,如果stdio的模式soap_begin_send()後但從std::cin讀取數據之前恢復到_O_TEXTgetline()工作正常。這裏是補丁:

_setmode(_fileno(stdin), _O_TEXT) 

回答

1

這是爲了避免在讀取和編寫Unicode和UTF-8編碼的XML時出現編碼問題。

+0

感謝您的回答。但是爲什麼gSOAP *會在這裏觸摸標準輸入流呢?它從文件流中讀取數據('soap-> is-> read()'被執行),而不是來自'stdio'。 gSOAP在'soap_begin_send()'之後以二進制模式離開'stdio',並且永遠不會將其恢復爲默認的文本模式。 – 2012-02-08 16:34:25

+0

您不需要修改recvfd以使用流。 soap-> is流優先於soap-> recvfd。 – 2012-02-28 21:20:33

1

此外,這也會導致不必要的副作用!通過使用

_setmode(soap-> recvfd,_O_BINARY); //在stdsoap2.cpp中找到

你總是假定從一個文件或標準輸入讀取,但是如果你設置了soap->是一個std :: istringstream,這是不正確的。

假設您在主例程中使用了getchar(),但是您嘗試從std:istringstream(使用gsoap xml綁定)反序列化xml的線程。結果你的程序會掛起。唯一的方法是設置soap-> recvfd = -1;

關於Ralph

+0

謝謝你的帖子,但它沒有給出答案。如果我們從'std :: istream'('soap-> is')讀取,我們不應該修改'recvfd'。我將這個問題發佈到gSOAP郵件列表,現在正在等待某人確認這是否是一個錯誤。 – 2012-02-09 15:42:05

+0

當我設置'soap-> recvfd = -1;'時,我的程序崩潰了,否則它會像您已經提到的那樣掛起。該怎麼辦? – foobar 2015-04-13 07:20:48

相關問題