Json output into single line - c++

Question:
With below sample code, able to create a json string and print as below, my requirements is to print into a single line(below as well) with no space or tab, can i do it using boost or need to explicitly remove from string.
#include "boost/property_tree/json_parser.hpp"
#include <boost/property_tree/ptree.hpp>
#include <sstream>
#include <iostream>
int main()
{
boost::property_tree::ptree root;
root.put("sessionId", "123456");
root.put("applicationName", "Sample_Appliation");
root.put("applicationId", "null");
root.put("logMessage", "Sample Message");
root.put("loggingTime", "2020-03-17T13:26:45.013");
std::stringstream ss;
boost::property_tree::write_json(ss, root);
std::cout << ss.str() << std::endl;
return 0;
}
Output:
{
"sessionId": "123456",
"applicationName": "Sample_Appliation",
"applicationId": "null",
"logMessage": "Sample Message",
"loggingTime": "2020-03-17T13:26:45.013"
}
Expected:
{"sessionId": "123456","applicationName": "Sample_Appliation","applicationId": "null","logMessage": "Sample Message","loggingTime": "2020-03-17T13:26:45.013"}

There is a third parameter to boost::property_tree::write_json named pretty with default value true, just pass false as a third parameter to disable pretty printing.

Big caution: Boost PropertyTree Is Not A JSON Library. However, as luck would have it, Boost has recently adopted the new JSON library, which actually does the job:
Live On Compiler Explorer
#include <boost/json.hpp>
#include <iostream>
int main() {
auto json = boost::json::parse(R"(
{
"sessionId": "123456",
"applicationName": "Sample_Appliation",
"applicationId": "null",
"logMessage": "Sample Message",
"loggingTime": "2020-03-17T13:26:45.013"
})");
std::cout << json;
}
Prints
{"sessionId":"123456","applicationName":"Sample_Appliation","applicationId":"null","logMessage":"Sample Message","loggingTime":"2020-03-17T13:26:45.013"}

Related

Poco failing to send mail

I have installed the Poco C++ library (Release 1.12.4) with vcpkg on my computer and I'm trying to send a simple Mail by using the sample delivered on the Github Page.`The script is compile with Visual Studio 17.
The full code:
#include "Poco/Net/MailMessage.h"
#include "Poco/Net/MailRecipient.h"
#include "Poco/Net/SecureSMTPClientSession.h"
#include "Poco/Net/StringPartSource.h"
#include "Poco/Net/SSLManager.h"
#include "Poco/Net/ConsoleCertificateHandler.h"
#include "Poco/Net/PrivateKeyPassphraseHandler.h"
#include "Poco/SharedPtr.h"
#include "Poco/Path.h"
#include "Poco/Exception.h"
#include <iostream>
using Poco::Net::MailMessage;
using Poco::Net::MailRecipient;
using Poco::Net::SMTPClientSession;
using Poco::Net::SecureSMTPClientSession;
using Poco::Net::StringPartSource;
using Poco::Net::SSLManager;
using Poco::Net::Context;
using Poco::Net::InvalidCertificateHandler;
using Poco::Net::ConsoleCertificateHandler;
using Poco::SharedPtr;
using Poco::Path;
using Poco::Exception;
using Poco::UInt16;
using namespace std;
class SSLInitializer
{
public:
SSLInitializer()
{
Poco::Net::initializeSSL();
}
~SSLInitializer()
{
Poco::Net::uninitializeSSL();
}
};
int main()
{
SSLInitializer sslInitializer;
std::string mailhost("smtp.office365.com");
std::string recipient("recipient#gmail.com");
std::string username("MyMail#gmail.com");
std::string password("MyPassword");
Poco:UInt16 port = 587;
try
{
// Note: we must create the passphrase handler prior Context
SharedPtr<InvalidCertificateHandler> pCert = new ConsoleCertificateHandler(false); // ask the user via console
Context::Ptr pContext = new Context(Context::CLIENT_USE, "");
SSLManager::instance().initializeClient(0, pCert, pContext);
MailMessage message;
message.addRecipient(MailRecipient(MailRecipient::PRIMARY_RECIPIENT, recipient));
message.setSubject("Hello from the POCO C++ Libraries");
std::string content;
content += "Hello ";
content += recipient;
content += ",\r\n\r\n";
content += "This is a greeting from the POCO C++ Libraries.\r\n\r\n";
message.addContent(new StringPartSource(content));
SecureSMTPClientSession session(mailhost, port);
session.login();
session.startTLS(pContext);
if (!username.empty())
{
session.login(SMTPClientSession::AUTH_LOGIN, username, password);
}
session.sendMessage(message);
session.close();
}
catch (Exception& exc)
{
std::cerr << exc.displayText() << std::endl;
return 1;
}
return 0;
}
I already tried the code on my first computer and it works. I wanted to try it on my laptop but it raises following error:
Poco::Net::NoCertificateException
and after a while:
SSL Exception: Error during handshake: failed to read data
I located the error and it comes from this line:
session.startTLS(pContext);
Thank you for helping me!
P.S: I don't use OpenSSL but NetSSL_Win, an implementation of the POCO NetSSL library based on Windows Schannel.

How to pipe/redirect the final stdout of a NCurse command?

I have a command that displays Ncurses stuffs (initscr, printw, addch, ...). That's okay.
At the end (endwin), I want to "output" (std::cout << "some string") a string to be processed by other command (or maybe redirected to a stream).
I want to do something like this :
my-ncurse-command | any-other-command
my-ncurse-command > some-stream
Problem is : my ncurses display is captured by the pipe or the redirect, not only the final string.
Is there a way to allow that ?
Thanks.
Instead of initscr(), use newterm(). If you are already using newterm it's just a matter of supplying a different output stream than stdout.
initscr() is equivalent to:
#include <cstdlib>
WINDOW* myinitscr() {
newterm(getenv("TERM"), stdout, stdin);
return stdscr;
}
so
#include <cstdio>
#include <cstdlib>
std::FILE* cursesout{};
WINDOW* myinitscr() {
cursesout = std::fopen("/dev/tty", "w"); // open new stream to your terminal
if(cursesout) newterm(std::getenv("TERM"), cursesout, stdin);
return stdscr;
}
and after endwin():
std::fclose(cursesout);
Alternatively, use a smart pointer to not have to std::fclose the new output stream manually:
#include <cstdio>
#include <cstdlib>
#include <memory>
using FILE_ptr = std::unique_ptr<FILE, decltype(&std::fclose)>;
FILE_ptr cursesout{nullptr, nullptr};
WINDOW* myinitscr() {
cursesout = FILE_ptr(std::fopen("/dev/tty", "w"), &std::fclose);
if(cursesout) newterm(std::getenv("TERM"), cursesout.get(), stdin);
return stdscr;
}
A version not taking the address of a standard library function (which is strictly prohibited) could look like this:
#include <cstdio>
#include <cstdlib>
#include <memory>
using FILE_ptr = std::unique_ptr<FILE, void (*)(FILE*)>;
FILE_ptr cursesout{nullptr, nullptr};
WINDOW* myinitscr() {
cursesout = FILE_ptr(std::fopen("/dev/tty", "w"), [](FILE* fp) {
std::fclose(fp);
});
if(cursesout) newterm(std::getenv("TERM"), cursesout.get(), stdin);
return stdscr;
}

How to play with spdlog?

I downloaded and followed the example 1.
Moved to example 2 (Create stdout/stderr logger object) and got stuck. Actually I can run it as it is but if I change
spdlog::get("console") to spdlog::get("err_logger") it crashes.
Am I supposed to change it like that?
#include "spdlog/spdlog.h"
#include "spdlog/sinks/stdout_color_sinks.h"
void stdout_example()
{
// create color multi threaded logger
auto console = spdlog::stdout_color_mt("console");
auto err_logger = spdlog::stderr_color_mt("stderr");
spdlog::get("err_logger")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name)");
}
int main()
{
stdout_example();
return 0;
}
I also tried Basic file logger example:
#include <iostream>
#include "spdlog/sinks/basic_file_sink.h"
void basic_logfile_example()
{
try
{
auto logger = spdlog::basic_logger_mt("basic_logger", "logs/basic-log.txt");
}
catch (const spdlog::spdlog_ex &ex)
{
std::cout << "Log init failed: " << ex.what() << std::endl;
}
}
int main()
{
basic_logfile_example();
return 0;
}
And I see it creates basic-log.txt file but there is nothing on it.
Because you need to register err_logger logger first. There is no default err_logger as far as I know. spdlog::get() returns logger based on its registered name, not variable.
You need a code like this. Code is complex and you may not need all of it though:
#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/sinks/rotating_file_sink.h"
void multi_sink_example2()
{
spdlog::init_thread_pool(8192, 1);
auto stdout_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt >();
auto rotating_sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt>("mylog.txt", 1024*1024*10, 3);
std::vector<spdlog::sink_ptr> sinks {stdout_sink, rotating_sink};
auto logger = std::make_shared<spdlog::async_logger>("err_logger", sinks.begin(), sinks.end(), spdlog::thread_pool(), spdlog::async_overflow_policy::block);
spdlog::register_logger(logger); //<-- this line registers logger for spdlog::get
}
and after this code, you can use spdlog::get("err_logger").
You can read about creating and registering loggers here.
I think spdlog::stderr_color_mt("stderr"); registers logger with name stderr so spdlog::get("stderr") may work, but have not tested myself.

How do I write a minimal GraphQL query of the Axie server?

I'm writing a program to download data about Axies and process them. My plan is to download all the marketplace, getting just the index numbers, then download details about Axies. Before getting all the details about an Axie, I'd like to get just one detail. I've succeeded in making an HTTPS connection to the server and sending a query, but all it replies is "Bad Request".
I've been using Shane Maglangit's site https://axie-graphql.web.app/ for examples, but the examples are too big for me to understand, since I don't know GraphQL or JSON, and part of the queries has literal \n and the other part has linefeeds, which is confusing me. His code is in JavaScript, which I don't know, so I don't know if JS is doing something different with \n than C++ does.
Here's my code:
main.cpp
#include <iostream>
#include <iomanip>
#include <boost/program_options.hpp>
#include "http.h"
using namespace std;
int main(int argc,char **argv)
{
string query="{\n \"operationName\": \"GetAxieDetail\",\n"
" \"variables\":\n {\n \"axieId\": \"5144540\"\n },\n"
" \"query\": \"query GetAxieDetail($axieId: ID!) {\\n ...AxieDetail\\n __typename}\n}"
"fragment AxieDetail on Axie{axie(axieId: $axieId)}\"";
string response;
string urlv2="https://axieinfinity.com/graphql-server-v2/graphql";
string urlv1="https://graphql-gateway.axieinfinity.com/graphql";
response=httpPost(urlv1,query);
cout<<response<<endl;
return 0;
}
http.h
#include <string>
std::string httpPost(std::string url,std::string data);
http.cpp
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/beast.hpp>
#include <boost/beast/ssl.hpp>
#include <boost/asio/ssl/error.hpp>
#include <boost/asio/ssl/stream.hpp>
#include <array>
#include <iostream>
namespace beast=boost::beast;
namespace http=beast::http;
namespace net=boost::asio;
namespace ssl=net::ssl;
using tcp=net::ip::tcp;
using namespace std;
array<string,4> parseUrl(string url)
// protocol, hostname, port, path. All are strings, including the port.
{
size_t pos0=url.find("://");
size_t pos1;
array<string,4> ret;
ret[0]=url.substr(0,pos0);
if (pos0<url.length())
pos0+=3;
pos1=url.find("/",pos0);
ret[1]=url.substr(pos0,pos1-pos0);
ret[3]=url.substr(pos1);
pos0=ret[1].find(":");
if (pos0<ret[1].length())
{
ret[2]=ret[1].substr(pos0+1);
ret[1]=ret[1].substr(0,pos0);
}
else
if (ret[0]=="https")
ret[2]="443";
else if (ret[0]=="https")
ret[2]="80";
else
ret[2]="0";
return ret;
}
string httpPost(string url,string data)
{
net::io_context context;
ssl::context ctx(ssl::context::tlsv12_client);
tcp::resolver res(context);
tcp::resolver::results_type endpoints;
beast::ssl_stream<beast::tcp_stream> stream(context,ctx);
array<string,4> parsed=parseUrl(url);
http::request<http::string_body> req;
http::response<http::string_body> resp;
beast::flat_buffer buffer;
//load_root_certificates(ctx);
ctx.set_verify_mode(ssl::verify_peer);
endpoints=res.resolve(parsed[1],parsed[2]);
beast::get_lowest_layer(stream).connect(endpoints);
SSL_set_tlsext_host_name(stream.native_handle(),parsed[1].c_str());
if (parsed[0]=="https")
stream.handshake(net::ssl::stream_base::client);
req.method(http::verb::post);
req.target(parsed[3]);
req.set(http::field::host,parsed[1]);
req.set(http::field::user_agent,BOOST_BEAST_VERSION_STRING);
req.set(http::field::content_type,"application/json");
req.set(http::field::accept,"application/json");
req.body()=data;
req.prepare_payload();
http::write(stream,req);
http::read(stream,buffer,resp);
cout<<parsed[0]<<"|\n"<<parsed[1]<<"|\n"<<parsed[2]<<"|\n"<<parsed[3]<<"|\n";
cout<<data<<"|\n";
return resp.body();
}
How can I write a query that returns one detail of the Axie with the specified number? Which of the two Axie servers should I use, and what's the difference?
Here is a working query string:
string query="{\n"
" \"operationName\": \"GetAxieDetail\",\n"
" \"variables\":\n"
" {\n"
" \"axieId\": \"5144540\"\n"
" },\n"
" \"query\":\n"
" \"query GetAxieDetail($axieId: ID!)"
" {\\n"
" axie(axieId: $axieId)\\n"
" {\\n"
" class\\n"
" }\\n"
" }\"\n"
"}\n";
The response is:
{"data":{"axie":{"class":"Plant"}}}
The server insists on no line feeds in the quoted query string, but allows \n; the \n, though, makes no difference, as the response is just one line.

How to use OpenSSL in POCO C++ library correctly

According to the Specification in POCO assistant:
Initialize the NetSSL library, as well as the underlying OpenSSL
libraries, by calling Poco::Crypto::OpenSSLInitializer::initialize().
Should be called before using any class from the NetSSL library.
The NetSSL will be initialized automatically, through
Poco::Crypto::OpenSSLInitializer instances or similar mechanisms
when creating Context or SSLManager instances.
However, it is recommended to call initializeSSL()
in any case at application startup.
When I want to use HTTPSClientSession,do I have to construct an Application object first?
How can I use it in Client? Any guy can tell me ?Thank you very much!
Let's take Net/samples/httpget as an example, let's copy httpget/ as a new httpsget directory:
open Makefile, add "PocoNetSSL" to target_libs
replace 'HTTPClientSession' with 'HTTPSClientSession'
you need to create Poco::Net::Context for SSL use
replace 'HTTPClientSession session(uri.getHost(), uri.getPort());' with following two lines:
const Context::Ptr context = new Context(Context::CLIENT_USE, "", "", "", Context::VERIFY_NONE, 9, false, "ALL:!ADH:!LOW:!EXP:!MD5:#STRENGTH");
HTTPSClientSession session(uri.getHost(), uri.getPort(), context);
Summary:
add PocoNetSSL as a lib_depends
use Poco::Net::Context with HTTPSClientSession
No, you do not need the Application object. Here's a fully functional example:
$ httpsget https://httpbin.org/user-agent
{
"user-agent": "Poco HTTPSClientSession"
}
Code:
#include "Poco/StreamCopier.h"
#include "Poco/URI.h"
#include "Poco/Exception.h"
#include "Poco/SharedPtr.h"
#include "Poco/Net/SSLManager.h"
#include "Poco/Net/KeyConsoleHandler.h"
#include "Poco/Net/ConsoleCertificateHandler.h"
#include "Poco/Net/HTTPSClientSession.h"
#include "Poco/Net/HTTPRequest.h"
#include "Poco/Net/HTTPResponse.h"
#include <memory>
#include <iostream>
using namespace Poco;
using namespace Poco::Net;
class SSLInitializer {
public:
SSLInitializer() { Poco::Net::initializeSSL(); }
~SSLInitializer() { Poco::Net::uninitializeSSL(); }
};
int main(int argc, char** argv)
{
SSLInitializer sslInitializer;
SharedPtr<InvalidCertificateHandler> ptrCert = new ConsoleCertificateHandler(false);
Context::Ptr ptrContext = new Context(Context::CLIENT_USE, "", "", "rootcert.pem", Context::VERIFY_STRICT, 9, false, "ALL:!ADH:!LOW:!EXP:!MD5:#STRENGTH");
SSLManager::instance().initializeClient(0, ptrCert, ptrContext);
try
{
if (argc > 1)
{
URI uri(argv[1]);
HTTPSClientSession s(uri.getHost(), uri.getPort());
HTTPRequest request(HTTPRequest::HTTP_GET, uri.getPath());
request.set("user-agent", "Poco HTTPSClientSession");
s.sendRequest(request);
HTTPResponse response;
std::istream& rs = s.receiveResponse(response);
StreamCopier::copyStream(rs, std::cout);
}
}
catch (Exception& ex)
{
std::cout << ex.displayText() << std::endl;
return 1;
}
return 0;
}