2012-02-08 1 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 установлен в 0 в soap_init() и 0 является значение файла дескриптора stdin.

После режима stdin «s изменяется на двоичном, библиотека STL не анализирует \r\n к одному \n для операций чтения и getline(cin, str), как обычно, читает все до \n, копирование \r в выходную строку. И это точно символ возврата каретки, который появляется в новых строках в конечном XML.

Мой вопрос: Почему gSOAP изменяет режим stdin, если источником данных является поток файлов? Это ошибка в gSOAP?

Примечание:

Как и следовало ожидать, если режим stdio «s будет вновь вернулась к _O_TEXT после soap_begin_send() но перед чтением данных из std::cin, getline() работает отлично. Вот патч:

_setmode(_fileno(stdin), _O_TEXT) 

ответ

1

Это необходимо для предотвращения проблем с кодированием при чтении и записи кодированных в кодировке Unicode и UTF-8.

+0

Спасибо за ваш ответ. Но зачем вообще использовать gSOAP * touch * стандартный поток ввода? Он считывает данные из потока файлов (выполняется 'soap-> is-> read()'), а не из 'stdio'. gSOAP оставляет 'stdio' в двоичном режиме после' soap_begin_send() 'и никогда не возвращает его обратно в текстовый режим по умолчанию. –

+0

Вам не нужно изменять recvfd для использования потоков. Мыло -> поток имеет приоритет над soap-> recvfd. –

1

Также это вызывает нежелательные побочные эффекты! Используя

_setmode (soap-> recvfd, _O_BINARY); // найдено в stdsoap2.cpp

вы всегда считаете, что хотите прочитать из файла или stdin, однако если вы установили soap-> в std :: istringstream, это не так.

Предположим, вы используете getchar() в своей основной подпрограмме, но в потоке вы пытаетесь десериализовать xml из std: istringstream (используя привязку xml gsoap). В результате ваша программа зависает. единственный способ - установить soap-> recvfd = -1;

С уважением, Ральф

+0

Спасибо за сообщение, но он не дает ответа. Мы не должны модифицировать 'recvfd', если мы читаем из' std :: istream' ('soap-> is'). Я отправил этот вопрос в список рассылки gSOAP и теперь жду кого-то, чтобы подтвердить, является ли это ошибкой или нет. –

+0

моя программа вылетает при установке 'soap-> recvfd = -1;', и в противном случае она зависает, как вы уже упоминали. что делать? – foobar

Смежные вопросы