将`boost::beast::multibuffer`转换为`std::istream`。

huangapple go评论61阅读模式
英文:

Convert boost::beast::multibuffer to std::istream

问题

I want to parse json content from it like this:

我想这样解析JSON内容:

英文:

I am getting boost::beast::multibuffer object from http::response<http::dynamic_body>::body() method. Then, I want to parse json content from it like this:

boost::property_tree::read_json(requestBodyStream, propertyTree);

Should I use boost::beast::buffers_to_string and std::stringstream to get requestBodyStream or is it possible to do without spending so much memory on copying the contents of the buffer?

答案1

得分: 1

总的来说,不要编写_具体实现_的程序,而要根据_概念_编写程序。在这里,dynamic_body 文档

> 这个主体使用DynamicBuffer作为保存消息有效载荷的基于内存的容器。使用这种主体类型的消息可以进行序列化和解析。

您不需要这个概念,因为您将完全在内存中使用它,但如果需要的话,可以像这样操作:

net::streambuf sb;
sb.commit(net::buffer_copy(sb.prepare(body.size()), body.cdata()));

std::istream is(&sb);
ptree doc;
read_json(is, doc);

Coliru 上查看实际示例

#include <boost/asio.hpp>
#include <boost/asio/posix/stream_descriptor.hpp>

#include <boost/beast/http.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <iostream>
namespace net = boost::beast::net;
namespace http = boost::beast::http;
using boost::property_tree::ptree;

int main() {
    net::posix::stream_descriptor input(net::system_executor{}, 0); // stdin

    http::response<http::dynamic_body> res;
    {
        net::streambuf readbuf;
        http::read(input, readbuf, res);
    }

    auto& body = res.body();

    net::streambuf sb;
    sb.commit(net::buffer_copy(sb.prepare(body.size()), body.cdata()));

    std::istream is(&sb);
    ptree doc;
    read_json(is, doc);

    write_json(std::cout << "Parsed body: ", doc);
}

这段代码从标准输入读取示例响应,例如:

HTTP/1.1 200 OK
Content-Length: 50

{"this":{"is":[1,2,3], "a":"sample"}, "object":42}

然后输出:

Parsed body: {
    "this": {
        "is": [
            "1",
            "2",
            "3"
        ],
        "a": "sample"
    },
    "object": "42"
}

但是

既然我们已经回答了问题,让我们添加一些背景信息:

  • 不要使用 Boost Property Tree(除非你需要 Property Trees。提示:你不需要)。看看输出:Property Tree 不是一个 JSON 库。

  • 除非需要,不要使用 dynamic body。在这种情况下,您正在将整个消息读入内存,将其在内存中复制(以转换为 streambuf),使用区域感知 istream 从中读取(较慢),并将结果作为另一个副本存储在内存中。

相反,使用尽可能简单的模型并使用 JSON 库,比如 Boost.JSON

Coliru 上查看实际示例

#include <boost/asio.hpp>
#include <boost/asio/posix/stream_descriptor.hpp>

#include <boost/beast/http.hpp>
#include <boost/json/src.hpp> // for header-only
#include <iostream>
namespace net  = boost::beast::net;
namespace http = boost::beast::http;
namespace json = boost::json;

int main() {
    net::posix::stream_descriptor input(net::system_executor{}, 0); // stdin

    http::response<http::string_body> res;
    {
        net::streambuf readbuf;
        http::read(input, readbuf, res);
    }

    auto doc = json::parse(res.body());
    std::cout << "Parsed body: " << doc << "\n";
}

这是更少的代码,更高效,最重要的是,正确处理了 JSON!

英文:

In general, don't program the specific implementation, but program to the concept. Here, dynamic_body documents:

> This body uses a DynamicBuffer as a memory-based container for holding message payloads. Messages using this body type may be serialized and parsed.

You don't need that concept, as you will be consuming this entirely in-memory anyways, but if you did, you would go about it like:

net::streambuf sb;
sb.commit(net::buffer_copy(sb.prepare(body.size()), body.cdata()));

std::istream is(&sb);
ptree doc;
read_json(is, doc);

See it Live On Coliru

#include <boost/asio.hpp>
#include <boost/asio/posix/stream_descriptor.hpp>

#include <boost/beast/http.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <iostream>
namespace net = boost::beast::net;
namespace http = boost::beast::http;
using boost::property_tree::ptree;

int main() {
    net::posix::stream_descriptor input(net::system_executor{}, 0); // stdin

    http::response<http::dynamic_body> res;
    {
        net::streambuf readbuf;
        http::read(input, readbuf, res);
    }

    auto& body = res.body();

    net::streambuf sb;
    sb.commit(net::buffer_copy(sb.prepare(body.size()), body.cdata()));

    std::istream is(&sb);
    ptree doc;
    read_json(is, doc);

    write_json(std::cout << "Parsed body: ", doc);
}

It reads a sample response from stdin, let's use

HTTP/1.1 200 OK
Content-Length: 50

{"this":{"is":[1,2,3], "a":"sample"}, "object":42}

Like so:

g++ -std=c++20 -O2 -Wall -pedantic -pthread main.cpp && ./a.out <<< $'HTTP/1.1 200 OK\r\nContent-Length: 50\r\n\r\n{\"this\":{\"is\":[1,2,3], \"a\":\"sample\"}, \"object\":42}'

Prints

Parsed body: {
    "this": {
        "is": [
            "1",
            "2",
            "3"
        ],
        "a": "sample"
    },
    "object": "42"
}

HOWEVER

Now that we've answered the question, let's add context:

  • Don't use Boost Property Tree (unless you need Property Trees. Hint: you do not). Look at the output: Property Tree is NOT a JSON library

  • Don't use dynamic body unless you need it. In this case you're reading the entire message in memory, copying it in memory (to convert to a streambuf), reading from it using locale-aware istream (slow) and the result lives as another copy in memory.

Instead, use the simplest model you can and use a JSON library, like, you know, Boost.JSON:

Live On Coliru

#include <boost/asio.hpp>
#include <boost/asio/posix/stream_descriptor.hpp>

#include <boost/beast/http.hpp>
#include <boost/json/src.hpp> // for header-only
#include <iostream>
namespace net  = boost::beast::net;
namespace http = boost::beast::http;
namespace json = boost::json;

int main() {
    net::posix::stream_descriptor input(net::system_executor{}, 0); // stdin

    http::response<http::string_body> res;
    {
        net::streambuf readbuf;
        http::read(input, readbuf, res);
    }

    auto doc = json::parse(res.body());
    std::cout << "Parsed body: " << doc << "\n";
}

It's less code, more efficient and most importantly, correct handling of the JSON!

huangapple
  • 本文由 发表于 2023年6月13日 02:54:35
  • 转载请务必保留本文链接:https://go.coder-hub.com/76459521.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定