How to parse json data from websocket_client using cpprestsdk - c++

I'm connecting to a WebSocket whom always replies in JSON. I see there is an extract_string method for websocket_incoming_message however after trying numerous things with json:value it seems as though you can only construct JSON arrays on-the-fly by inserting key-value pairs one-by-one. Am I missing something here or is there a way to take the output from websocket_incoming_message and directly convert it into a json:value array?
websocket_client client;
//start socket connection to server
try {
std::cout << "s
----------
client.connect(U("wss://XZXXXZZy.com/ws?account_id=4de3f308f2f8d3247As70228f94e0d2aAea&ws_key=reception")).wait();
}
catch (const std::exception&e)
{
std::cout << e.what() << std::endl;
}
//send messages to the server
//websocket_outgoing_message msg;
//msg.set_pong_message();
//std::cout << "\n...........2nd.........;";
//std::string data = "hii";
//client.send(msg).then([]() {
//
//
//
//
// /* Successfully sent the message. */ });
//std::cout << " Successfully sent the message.";
//std::cout << "\n...........3rd.........;";
//receive messages from the server
client.receive().then([](websocket_incoming_message msg) {
std::cout << "receiving data from socket";
return msg.extract_string();
}).then([](std::string body) {
//FETCHING THE DATA FROM BODY. "TEXT/JSON"
std::cout << "displaying the data";
std::cout << body << std::endl;
const json::value& v1 = body.substr;
utility::string_t jsonval = v1.serialize();
auto array = v1.at(U("rows")).as_array();
for (int i = 0; i<array.size(); ++i)
{
auto id = array[i].at(U("id")).as_string();
std::wcout << "\n" << id;
auto key = array[i].at(U("key")).as_string();
std::wcout << "\n" << key;
auto array2 = array[i].at(U("value")).as_array();
std::wcout << array2[0];
std::wcout << array2[1];
}
}
);
//close the connection
client.close().then([]() {
std::cout << "successfully close socket connction";
/* Successfully closed the connection. */
});
I have json response in my string body.but i dont know how to parse json data from websocket responses event. i want to display contacts from api responses.please help me..
MY JSON RESPONSES
--------------------------------------
.{"action":"refresh_dashboard","data":{"users_list":[{"user_id":"901e6076ff351cfc2195fb86f8438a26","extensions":["1002"],"name":"Karthik M"},{"user_id":"cc3f94ecc14ee9c55670dcde9adc1887","extensions":["1006"],"name":"Rounak S Kiran"},{"user_id":"6c29ebdb34e1761fdf9423c573087979","extensions":["1003"],"name":"Amar Nath"},{"user_id":"74d5b5a9aca1faa4c2f217ce87b621d8","extensions":["1008"],"name":"Robin Raju"},{"user_id":"a7ad7e73bf93ea83c8efdc1723cba198","extensions":["1007"],"name":"Arshad Arif"},{"user_id":"b55146df593ec8d09e5fe12a8a4c1108","extensions":["1001"],"name":"Rahib Rasheed"},{"user_id":"3258f7ae4ae1db60435cbcf583f64a89","extensions":["1009"],"name":"Test User"},{"user_id":"90bc84e5e8a3427fe35e99bd4386de95","extensions":["1010"],"name":"Prince T"},{"user_id":"b501ef5b270a196afc0eed557ca74237","extensions":["1005","+17325951060"],"name":"Jineed AJ"},{"user_id":"1422af351e06adeab2de92f5a633a444","extensions":["1004"],"name":"Ashok PA"}],"busy_users":[],"reg_users":[{"user_id":"cc3f94ecc14ee9c55670dcde9adc1887","status":"registered"},{"user_id":"901e6076ff351cfc2195fb86f8438a26","status":"registered"},{"user_id":"1422af351e06adeab2de92f5a633a444","status":"registered"},{"user_id":"3258f7ae4ae1db60435cbcf583f64a89","status":"registered"},{"user_id":"b55146df593ec8d09e5fe12a8a4c1108","status":"registered"},{"user_id":"6c29ebdb34e1761fdf9423c573087979","status":"registered"}],"contacts":[{"owner_id":"cc3f94ecc14ee9c55670dcde9adc1887","status":"ready"},{"owner_id":"901e6076ff351cfc2195fb86f8438a26","status":"ready"},{"owner_id":"1422af351e06adeab2de92f5a633a444","status":"ready"},{"owner_id":"3258f7ae4ae1db60435cbcf583f64a89","status":"ready"},{"owner_id":"b55146df593ec8d09e5fe12a8a4c1108","status":"ready"},{"owner_id":"6c29ebdb34e1761fdf9423c573087979","status":"ready"}]}}

I got the complete solution .please try to use boost pacakges from nuget. The documentation will help you to parse the json data from string. I think jsoncpp is not an updated packages available in the nuget.so please try boost packages available in the nuget.
MYJSON STRING
{"action":"refresh_dashboard","data":{"users_list":[{"user_id":"901e6076ff351cfc2195fb86f8438a26","extensions":["1002"],"name":"Karthik M"},{"user_id":"7d617ef5b2390d081d901b0d5cd108eb","extensions":["1015"],"name":"Synway User2"},{"user_id":"c8f667f7d663e81f6e7fa34b9296f067","extensions":["1012"],"name":"Rahib Video"},{"user_id":"cc3f94ecc14ee9c55670dcde9adc1887","extensions":["1006"],"name":"Rounak S Kiran"},{"user_id":"6c29ebdb34e1761fdf9423c573087979","extensions":["1003"],"name":"Amar Nath"},{"user_id":"8e15c2d95d4325cb07f0750846966be8","extensions":["1011"],"name":"TLS User"},{"user_id":"2fc4142bdacf83c1957bda0ad9d50e3d","extensions":["1014"],"name":"Synway User1"},{"user_id":"74d5b5a9aca1faa4c2f217ce87b621d8","extensions":["1008"],"name":"Robin Raju"},{"user_id":"a7ad7e73bf93ea83c8efdc1723cba198","extensions":["1007"],"name":"Arshad Arif"},{"user_id":"b55146df593ec8d09e5fe12a8a4c1108","extensions":["1001"],"name":"Rahib Rasheed"},{"user_id":"391391de005a8f5403c7b5591f462ea1","extensions":["1013"],"name":"Sangeeth J"},{"user_id":"3258f7ae4ae1db60435cbcf583f64a89","extensions":["1009"],"name":"Aby TL"},{"user_id":"90bc84e5e8a3427fe35e99bd4386de95","extensions":["1010"],"name":"Prince T"},{"user_id":"b501ef5b270a196afc0eed557ca74237","extensions":["1005"],"name":"Jineed AJ"},{"user_id":"1422af351e06adeab2de92f5a633a444","extensions":["1004"],"name":"Ashok PA"}],"busy_users":[],"reg_users":[{"user_id":"901e6076ff351cfc2195fb86f8438a26","status":"registered"},{"user_id":"6c29ebdb34e1761fdf9423c573087979","status":"registered"}],"contacts":[{"owner_id":"901e6076ff351cfc2195fb86f8438a26","status":"ready"},{"owner_id":"6c29ebdb34e1761fdf9423c573087979","status":"ready"}]}}
CODES
client.receive().then([](websocket_incoming_message msg) {
std::cout << "receiving data from socket";
// msg.message_type();
return msg.extract_string();
//1..i have one string
//cout<<"\n///////////test"<< msg.extract_string().get().c_str();
// // 2.convert to json array
//json::value::parse( ::to_string_t(msg.extract_string().get()))
//
}).then([](std::string body) {
//std::cout << "displaying the data";
std::cout << body << std::endl;
std::string ss = body;
ptree pt;
std::istringstream is(ss);
read_json(is, pt);
std::cout <<"\n 1st"<< "action: " << pt.get<std::string>("action") << "\n";
std::cout <<"\n 2nd"<< "data: " << pt.get<std::string>("data") << "\n";
std::cout << "--------------------------------------------------------------";
for (auto& e : pt.get_child("data.users_list")) {
std::cout << "\n" << "users id " << e.second.get<std::string>("user_id") << "\n";
}
});
useful resources
Parse JSON array as std::string with Boost ptree
C++ boost parse dynamically generated json string (not a file)

Related

Send request from a server to a connected client with RSocket C++

could someone kindly help me with the RSocket issue? I'm trying to send a request from the Server to a connected Client, but the Client Responder does not fire callbacks. I'm using the RSocket-cpp library.
Server code:
TcpConnectionAcceptor::Options opts;
opts.address = folly::SocketAddress("::", FLAGS_port);
opts.threads = 2;
// RSocket server accepting on TCP
auto rs = std::make_unique<TcpConnectionAcceptor>(std::move(opts));
auto serverThread = std::thread([&rs] {
// start accepting connections
rs->start(
[](std::unique_ptr<DuplexConnection> connection, folly::EventBase& eventBase) {
LOG(INFO) << "new incoming connected" << std::endl;
auto client = RSocket::createClientFromConnection(
std::move(connection), *eventBase.getEventBase(), SetupParameters(), nullptr, std::make_shared<GenericRequestResponseResponder>());
LOG(INFO) << "send data" << std::endl;
client->getRequester()->requestResponse(Payload("hello2"))->subscribe([](Payload p) {
LOG(INFO) << "Received1 >> " << p.moveDataToString() << std::endl;
});
LOG(INFO) << "request is sent from server" << std::endl;
});
});
Output:
I0327 07:11:33.583813 23622 RequestResponseHelloWorld_Server.cpp:95] new incoming connected
I0327 07:11:33.602982 23622 RequestResponseHelloWorld_Server.cpp:100] send data
I0327 07:11:33.604566 23622 RequestResponseHelloWorld_Server.cpp:105] request is sent from server
Client code:
class GenericRequestResponseResponder : public rsocket::RSocketResponder
{
public:
std::shared_ptr<Single<Payload>> handleRequestResponse(
Payload request,
StreamId /*streamId*/) override
{
LOG(INFO) << "GenericRequestResponseResponder.handleRequestResponse "
<< request << std::endl;
// string from payload data
auto requestString = request.moveDataToString();
return Single<Payload>::create(
[name = std::move(requestString)](auto subscriber) {
std::stringstream ss;
ss << "Ack " << name << "!";
std::string s = ss.str();
subscriber->onSubscribe(SingleSubscriptions::empty());
subscriber->onSuccess(Payload(s, "metadata"));
});
}
void handleFireAndForget(
rsocket::Payload request,
rsocket::StreamId /*streamId*/) override
{
LOG(INFO) << "GenericRequestResponseResponder.handleRequestResponse "
<< request << std::endl;
}
};
folly::SocketAddress address{folly::SocketAddress(host, port)};
std::shared_ptr<TelemetryConnection> connection{nullptr};
RSocket::createConnectedClient(
std::make_unique<TcpConnectionFactory>(
*m_worker->getEventBase(), std::move(address)), SetupParameters(), std::make_shared<GenericRequestResponseResponder>())
.thenValue([this, host, port, &connection](auto&& client) {
LOG(INFO) << "client is created" << std::endl;
m_clientList.append(client);
})
.thenError(
folly::tag_t<std::exception>{},
[&](const std::exception&) {
LOG(ERROR) << "connection failed";
}).get();
I was expecting GenericRequestResponseResponder::``handleRequestResponse fired when the server sends the request, but client output is empty

Turn off JSON value reorder in cpprestsdk

I'm using Microsoft's cpprestsdk to send a JSON from client to server.
The problem is that the data I'm writing in the JSON isn't keeping it's original writing order, and it's being rearranged in alphabetical order.
This is the function that returns the JSON value object:
web::json::value returnJSON()
{
json::value output;
output[L"Outer_list"][L"valX"] = json::value::string(L"value1");
output[L"Outer_list"][L"valY"] = json::value::string(L"value2");
output[L"Outer_list"][L"valA"] = json::value::string(L"value3");
output[L"Outer_list"][L"valZ"] = json::value::string(L"value4");
output[L"Outer_list"][L"valXList"][0] = json::value::string(L"XValue1");
output[L"Outer_list"][L"valXList"][1] = json::value::string(L"XValue2");
output[L"Outer_list"][L"valXList"][2] = json::value::string(L"XValue3");
output[L"Outer_list"][L"valYList"][0] = json::value::string(L"YValue1");
output[L"Outer_list"][L"valYList"][1] = json::value::string(L"YValue2");
output[L"Outer_list"][L"valYList"][2] = json::value::string(L"YValue3");
std::wcout << "output = " << output.serialize() << std::endl << std::endl;
return output;
}
And this is the function that sends the data:
void sendPOST()
{
web::json::value myJson = returnJSON();
http_client client(L"http://127.0.0.1:34568/");
try {
client.request(methods::POST, L"MY_path-query_fragment", myJson).then([](http_response response) {
if (response.status_code() == status_codes::OK) {
auto body = response.extract_string().get();
std::wcout << "The response is = \n" << body << std::endl;
}
else
{
std::cout << "No response" << std::endl;
}
});
}
catch (const std::exception& e)
{
std::cout << "ERROR: " << e.what() << std::endl;
}
}
The data should look like this:
{"Outer_list":{"valX":"value1","valY":"value2","valA":"value3","valZ":"value4","valXList":["XValue1","XValue2","XValue3"],"valYList":["YValue1","YValue2","YValue3"]}}
But on the client/server I see that the data that's being sent/received is:
{"Outer_list":{"valA":"value3","valX":"value1","valXList":["XValue1","XValue2","XValue3"],"valY":"value2","valYList":["YValue1","YValue2","YValue3"],"valZ":"value4"}}
As you can immediately see, the valA is the first one and the valZ is the last, becuase they have been reodered.
How can I turn off this alphabetical reoder, and keep the original writing order?
It turns out you can turn off the sorting! Add this line right after you create 'output':
output[L"Outer_list"] = web::json::value::object(true);
There is a default parameter (bool keep_order = false) that you can override when you create your web::json::value::object. Once this is set, all values added will be kept in the original order.
I was also looking for a way to make the output easier to read by humans and stumbled upon this in the docs.

How to convert boost beast multi_buffer to string?

I copy websocket example from boost::beast website and run it Websocket session work fine but I don't know how to convert received multi_buffer to string.
below code is websocket session handler.
void
do_session(tcp::socket &socket) {
try {
// Construct the stream by moving in the socket
websocket::stream <tcp::socket> ws{std::move(socket)};
// Accept the websocket handshake
ws.accept();
while (true) {
// This buffer will hold the incoming message
boost::beast::multi_buffer buffer;
// Read a message
boost::beast::error_code ec;
ws.read(buffer, ec);
if (ec == websocket::error::closed) {
break;
}
// Echo the message back
ws.text(ws.got_text());
ws.write(buffer);
}
cout << "Close" << endl;
}
catch (boost::system::system_error const &se) {
// This indicates that the session was closed
if (se.code() != websocket::error::closed)
std::cerr << "Error: " << se.code().message() << std::endl;
}
catch (std::exception const &e) {
std::cerr << "Error: " << e.what() << std::endl;
}
}
Is there way to convert buffer to string ?
Since original question was about converting to string directly, without using streams, I decided to add my answer.
You can use beast::buffers_to_string(buffer.data()).
You can use buffers on buffer.data()
std::cout << "Data read: " << boost::beast::buffers(buffer.data()) <<
std::endl;

Sending data through ZeroMQ (zmqpp) using MsgPack gives 'msgpack::v1::insufficient_bytes' error

I made a PUB/SUB connection using zmqpp and now I want to send data from the publisher to the subscribers using the header-only, C++11 version of msgpack-c.
The publisher has to send 2 int64_t numbers -- header_1 and header_2 -- followed by a std::vector<T> -- data --, where T is determined by the (header_1, header_2) combination.
Sinse there aren't that many examples on how to combine msgpack and zmqpp, the idea I came up with is to send a 3-part message by using zmqpp::message::add/add_raw. Each part would be packed/unpacked using msgpack.
The publisher packs a single data part as follows:
zmqpp::message msg;
int64_t header_1 = 1234567;
msgpack::sbuffer buffer;
msgpack::pack(buffer, header_1);
msg.add(buffer.data(), buffer.size());
And the receiver unpacks it like this:
zmqpp::message msg;
subscriberSock.receive(msg);
int64_t header_1;
msgpack::unpacked unpackedData;
// crash !
msgpack::unpack(unpackedData,
static_cast<const char*>(msg.raw_data(0)),
msg.size(0));
unpackedData.get().convert(&header_1);
When I run the code, I get the following error on the subscriber side:
terminate called after throwing an instance of 'msgpack::v1::insufficient_bytes'
what(): insufficient bytes
Aborted
Also, it seems that zmqpp has generated a 5-part message, even though I called add() only 3 times.
Q1: Am I packing/unpacking the data correctly ?
Q2: Is this the proper method for sending msgpack buffers using zmqpp ?
Here are the important parts of the code:
Publisher
zmqpp::socket publisherSock;
/* connection setup stuff ...*/
// forever send data to the subscribers
while(true)
{
zmqpp::message msg;
// meta info about the data
int64_t header_1 = 1234567;
int64_t header_2 = 89;
// sample data
std::vector<double> data;
data.push_back(1.2);
data.push_back(3.4);
data.push_back(5.6);
{
msgpack::sbuffer buffer;
msgpack::pack(buffer, header_1);
msg.add(buffer.data(), buffer.size());
cout << "header_1:" << header_1 << endl; // header_1:1234567
}
{
msgpack::sbuffer buffer;
msgpack::pack(buffer, header_2);
msg.add(buffer.data(), buffer.size());
cout << "header_2:" << header_2 << endl; // header_2:89
}
{
msgpack::sbuffer buffer;
msgpack::pack(buffer, data);
msg.add_raw(buffer.data(), buffer.size());
std::cout << "data: " << data << std::endl; // data:[1.2 3.4 5.6]
}
std::cout << msg.parts() << " parts" << std::endl; // prints "5 parts"... why ?
publisherSock.send(msg);
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
Subscriber
zmqpp::socket subscriberSock;
/* connection setup stuff ...*/
zmqpp::message msg;
subscriberSock.receive(msg);
int64_t header_1;
int64_t header_2;
std::vector<double> data;
std::cout << msg.parts() << " parts" << std::endl; // prints "5 parts"
{
// header 1
{
msgpack::unpacked unpackedData;
// crash !
msgpack::unpack(unpackedData,
static_cast<const char*>(msg.raw_data(0)),
msg.size(0));
unpackedData.get().convert(&header_1);
cout << "header_1:" << header_1 << endl;
}
// header 2
{
msgpack::unpacked unpackedData;
msgpack::unpack(unpackedData,
static_cast<const char*>(msg.raw_data(1)),
msg.size(1));
unpackedData.get().convert(&header_2);
cout << "header_2:" << header_2 << endl;
}
// data
{
msgpack::unpacked unpacked_data;
msgpack::unpack(unpacked_data,
static_cast<const char*>(msg.raw_data(2)),
msg.size(2));
unpacked_data.get().convert(&data);
std::cout << "data:" << data << std::endl;
}
}
EDIT: Problem solved: As pointed out by #Jens, the correct way of packing/sending data is by using zmqpp::message::add_raw()
zmqpp::message msg;
int64_t header_1 = 1234567;
msgpack::sbuffer buffer;
msgpack::pack(buffer, header_1);
msg.add_raw(buffer.data(), buffer.size());
I think the calls to msg.add(buffer.data(), buffer.size()do not add a array of buffer.size() bytes, but call message::add(Type const& part, Args &&...args), which
msg << buffer.data(), which probably calls message::operator<<(bool) since a pointer converts to bool
add(buffer.size()) which then calls msg << buffer.size(), which adds a size_t value as the next part.
Looking at the zmqpp::message class, using message::add_raw should do the trick.
PS: This is all without any guarantee because I have never used zmqpp or msgpack.

Code working inline, but when in class

I'm writing program using Boost::Asio, I want to implement simple chat. Currently I'm struggling with problem that when I put some code inline of class function it's working, but when same code is provided by another class object is not. I think it could be connected to Boost::Asio, but I'm not sure:
void Connection::session(socket_ptr sock)
{
try{
for(;;) {
char mesg[1024];
boost::system::error_code error;
size_t length = sock->read_some(boost::asio::buffer(mesg), error);
if (error == boost::asio::error::eof){
disconnected(sock);
break;
}
else if (error)
throw boost::system::system_error(error);
message msg(mesg,length);
char *data;
data = msg.getData();
std::cout << "In session " << strlen(data) << " " << data;
/*This is working
string s_msg,s_data;
s_msg = mesg;
s_data = s_msg.substr(2,length);
std::vector<char> writable(s_data.size() + 1);
std::copy(s_data.begin(), s_data.end(), writable.begin());
std::cout << "In session " << strlen(&writable[0]) << " " << &writable[0];
send_all(sock,&writable[0],strlen(&writable[0]));
*/
send_all(sock,data,strlen(data));
}
}
catch (std::exception& e){
std::cerr << "Exception in thread: " << e.what() << "\n";
}
}
Class message that is only parsing data
message::message(char *c_msg, size_t length)
{
msg = c_msg;
id = msg.at(0);
data = msg.substr(2,length);
}
char* message::getData()
{
std::vector<char> writable(data.size() + 1);
std::copy(data.begin(), data.end(), writable.begin());
std::cout << data;
std::cout << &writable[0];
return &writable[0];
}
So when using class message this line:
std::cout << "In session " << strlen(data) << " " << data;
I get:
st String //this is cout from message getData
st String //this is cout from message getData
In session 0
With inline version:
In session 11 st String
So, in session function string is empty although message cout shows something opposite.
I don't know if it's important, but this function is invoked as new thread from main.
Regards,
Piotr
You're returning the address of a temporary:
char* message::getData()
{
std::vector<char> writable(data.size() + 1);
//...
return &writable[0];
}
This is undefined behaviour.
I'm assuming that data is just a std::string. You could do this instead:
const char* message::getData() const
{
return data.c_str();
}