英文:
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);
#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:
#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:
#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!
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论