如果从文件流读取数据,为什么 gSOAP 将 stdin 模式设置为二进制?

发布于 2025-01-03 11:03:41 字数 6643 浏览 0 评论 0原文

我一直在尝试 gSOAP XML 数据绑定,方法是将 XML 文档加载到 C++ 类中,修改数据并将其序列化回 XML。

以下是 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->recvfdsoap_init() 中设置为 00 是文件描述符的值标准输入

一旦 stdin 的模式更改为二进制,STL 库不会将 \r\n 解析为单个 \n 进行读取操作,并且getline(cin, str) 像往常一样,读取 \n 之前的所有内容,并将 \r 复制到输出字符串中。这正是出现在最终 XML 的新字符串中的回车符。

我的问题是:如果数据源是文件流,为什么 gSOAP 会修改 stdin 模式?这是 gSOAP 中的错误吗?

注意:

正如预期的那样,如果在 soap_begin_send() 之后但在从 std 读取数据之前,stdio 的模式恢复为 _O_TEXT: :cin, getline() 工作正常。这是补丁:

_setmode(_fileno(stdin), _O_TEXT)

I've been playing with gSOAP XML data binding by loading XML document into C++ class, modifying data and serializing it back into XML.

Here's the snippet of the 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>

The following code loads XML into object, modifies object and serializes it back into XML.
Note that XML is loaded from a file, via file stream and that data to be added is obtained from a user, via 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;
}

After running this test application, everything is fine apart from all newly added elements have strings that terminate with a Carriage Return character (CR - ):

Modified XML looks like this:

<?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
" author="Scott Meyers
" title="Effective C++
">
            <gt:CopiesAvailable>123</gt:CopiesAvailable>
        </gt:Book>
    </gt:Books>
    ...
</gt:Library>

I traced the source of the bug and found the following:

soap_read__gt__Library() calls soap_begin_send() which executes the following line:

_setmode(soap->recvfd, _O_BINARY);

soap->recvfd is set to 0 in soap_init() and 0 is a value of file descriptor of stdin.

Once stdin's mode is changed to a binary, STL library does not parse \r\n to a single \n for read operations and getline(cin, str), as usual, reads everything up to \n, copying \r into output string. And that is exactly Carriage Return character which appears in new strings in the final XML.

My question is: Why does gSOAP modify stdin mode if the data source is a file stream? Is this a bug in gSOAP?

NOTE:

As expected, if stdio's mode is reverted back to _O_TEXT after soap_begin_send() but before reading data from std::cin, getline() works fine. Here is the patch:

_setmode(_fileno(stdin), _O_TEXT)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

爱她像谁 2025-01-10 11:03:42

这是为了避免在读写 Unicode 和 UTF-8 编码的 XML 时出现编码问题。

This is to avoid encoding problems when reading and writing Unicode and UTF-8 encoded XML.

云朵有点甜 2025-01-10 11:03:42

这也会导致不必要的副作用!通过使用

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

您总是假设从文件或标准输入中读取,但是如果您将soap->is设置为std::istringstream,则事实并非如此。

假设您在主例程中使用 getchar(),但在线程上尝试从 std:istringstream 反序列化 xml(使用 gsoap xml 绑定)。结果你的程序将会挂起。唯一的方法是设置soap->recvfd = -1;

问候拉尔夫

Also this cause unwanted side effects! By using

_setmode(soap->recvfd, _O_BINARY); // found in stdsoap2.cpp

you always assume to read from a file or stdin, however if you set soap->is to a std::istringstream this isn't true.

Just assume you are using getchar() in your main routine, but on a thread you try to deserialize a xml from a std:istringstream (using gsoap xml binding). As result your program will hang. the only way around is to set soap->recvfd = -1;

Regards Ralph

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文