Accessing JSON values in C++ - c++

I am trying to write a program that navigates your local disc in Unreal Engine for a small application. I have put together a REST server using Gradle, and long story short, I am given a JSON with a machines directories. I want to pull out the specific directories names, to be returned as string (FText specifically, but that not too important here) array.
I found a library created by nLohmann on github (https://github.com/nlohmann/json) which seems to be the best way to handle a JSON in c++. For the life of me, however, I can't figure out how to pull the directory names out. I've tried an iterator and a straightforward .value() call.
The code and a JSON example are below, any insight would be greatly appreciated.
char buffer[1024];
FILE *lsofFile_p = _popen("py C:\\Users\\jinx5\\CWorkspace\\sysCalls\\PullRoots.py", "r");
fgets(buffer, sizeof(buffer), lsofFile_p);
_pclose(lsofFile_p);
std::string rootsJson(buffer);
string s = rootsJson.substr(1);
s = ReplaceAll(s, "'", "");
//here my string s will contain: [{"description":"Local Disk","name":"C:\\"},{"description":"Local Disk","name":"D:\\"},{"description":"CD Drive","name":"E:\\"}]
//These are two syntax examples I found un nlohmann's docs, neither seems to work
auto j = json::parse(s);
string descr = j.value("description", "err");

I think your problem comes from number of \ in your literal string. You need 5 \ for C:\\ : C:\\\\\.
Here is a working example :
#include "json.hpp"
#include <string>
using namespace std;
using json = nlohmann::json;
int main(){
json j = json::parse("[{\"description\":\"Local Disk\",\"name\":\"C:\\\\\"},{\"description\":\"Local Disk\",\"name\":\"D:\\\\\"},{\"description\":\"CD Drive\",\"name\":\"E:\\\\\"}]");
cout << j.is_array() << endl;
for (auto& element : j) {
std::cout << "description : " << element["description"] << " | " << " name : " << element["name"] << '\n';
}
return 0;
}

Related

Error in JSON string creation with / operator using jsoncpp library

I am using JSONcpp library and facing problem to create json which contains / operator (like date : 02/12/2015). Below is my code:
JSONNODE *n = json_new(JSON_NODE);
json_push_back(n, json_new_a("String Node", "02/05/2015"));
json_char *jc = json_write_formatted(n);
printf("%s\n", jc);
json_free(jc);
json_delete(n);
Output :
{
"String Node":"02\/05\/2015"
}
Check here "\/" in date, we want only "/" in date, so my expected output should look like this:
Expected output:
{
"String Node":"02/05/2015"
}
How to get rid of it? We are using inbuilt library function and we can not modify library.
According to this example
The json_new_a method will escape your string values when you go to
write the final string.
The additional backslashes you are facing in the output are the result of adding escape characters. This is actually a good thing as it prevents both accidental and intentional errors.
I suppose the easies option for you would be to replace all \/ occurrences in output string with single /. I wrote a simple program to do so:
#include <iostream>
#include <string>
#include <boost/algorithm/string/replace.hpp>
using namespace std;
int main()
{
//Here's the "json_char *jc" from your code
const char* jc = "02\\/05\\/2015";
//Replacing code
string str(jc);
boost::replace_all(str, "\\/", "/");
//Show result
cout << "Old output: " << jc << endl;
cout << "New output: " << str << endl;
}
Live demo
BTW: libJSON is not really a C++ library - it's much more C-style which involves many low-level operations and is more complicated and obfuscated than more C++ish libraries. If you'd like to try, there is a nice list of C++ JSON libs here.

What is the most appropriate way to concatenate with MFC's CString

I am somewhat new to C++ and my background is in Java. I am working on a hdc printing method.
I would like to know the best practice for concatenating a combination of strings and ints into one CString. I am using MFC's CString.
int i = //the current page
int maxPage = //the calculated number of pages to print
CString pages = ("Page ") + _T(i) + (" of ") + _T(maxPage);
I would like it to look like 'Page 1 of 2'. My current code does not work. I am getting the error:
Expression must have integral or enum type
I have found more difficult ways to do what I need, but I want to know if there is a simple way similar to what I am trying. Thanks!
If that's MFC's CString class, then you probably want Format which is a sprintf-alike for it:
CString pages;
pages.Format(_T("Page %d of %d"), i, maxPage);
i.e. you can assemble the string using regular printf-format specifiers substituting in the numbers at runtime.
You can use also stringstream classes
#include <sstream>
#include <string>
int main ()
{
std::ostringstream textFormatted;
textFormatted << "Page " << i << " of " << maxPage;
// To convert it to a string
std::string s = textFormatted.str();
return 0;
}
std::string has all you need:
auto str = "Page " + std::to_string(i) + " of " + std::to_string(maxPage);
As stated correctly in the comment, you can access the underlying C-string via str.c_str(). Here is a live working example.
If you have C++11 you can use std::to_string: std::string pages = std::string("Page ") + std::to_string(i) + (" of ") + std::to_string(maxPage);
If you don't have C++11 you can use an ostringstream or boost::lexical_cast.

C++ boost/regex regex_search

Consider the following string content:
string content = "{'name':'Fantastic gloves','description':'Theese gloves will fit any time period.','current':{'trend':'high','price':'47.1000'}";
I have never used regex_search and I have been searching around for ways to use it - I still do not quite get it. From that random string (it's from an API) how could I grab two things:
1) the price - in this example it is 47.1000
2) the name - in this example Fantastic gloves
From what I have read, regex_search would be the best approach here. I plan on using the price as an integer value, I will use regex_replace in order to remove the "." from the string before converting it. I have only used regex_replace and I found it easy to work with, I don't know why I am struggling so much with regex_search.
Keynotes:
Content is contained inside ' '
Content id and value is separated by :
Conent/value are separated by ,
Value of id's name and price will vary.
My first though was to locate for instance price and then move 3 characters ahead (':') and gather everything until the next ' - however I am not sure if I am completely off-track here or not.
Any help is appreciated.
boost::regex would not be needed. Regular expressions are used for more general pattern matching, whereas your example is very specific. One way to handle your problem is to break the string up into individual tokens. Here is an example using boost::tokenizer:
#include <iostream>
#include <string>
#include <boost/tokenizer.hpp>
#include <map>
int main()
{
std::map<std::string, std::string> m;
std::string content = "{'name':'Fantastic gloves','description':'Theese gloves will fit any time period.','current':{'trend':'high','price':'47.1000'}";
boost::char_separator<char> sep("{},':");
boost::tokenizer<boost::char_separator<char>> tokenizer(content, sep);
std::string id;
for (auto tok = tokenizer.begin(); tok != tokenizer.end(); ++tok)
{
// Since "current" is a special case I added code to handle that
if (*tok != "current")
{
id = *tok++;
m[id] = *tok;
}
else
{
id = *++tok;
m[id] = *++tok; // trend
id = *++tok;
m[id] = *++tok; // price
}
}
std::cout << "Name: " << m["name"] << std::endl;
std::cout << "Price: " << m["price"] << std::endl;
}
Link to live code.
As the string you are attempting to parse appears to be JSON (JavaScript Object Notation), consider using a specialized JSON parser.
You can find a comprehensive list of JSON parsers in many languages including C++ at http://json.org/. Also, I found a discussion on the merits of several JSON parsers for C++ in response to this SO question.

get URL params with Poco library

I'm developing a web server with Poco library.
When my server receive a HTTP request with form data in GET mode, I don't know how to use the class HTMLForm to show a list with received pairs param=value.
With request.getURI().getQuery() I am able to get the complete string.
I guess I can split the string in the traditional way, using a tokenizer.
Is there a better way to do it using Poco?
Thanks
Ok, class HTMLForm inherits from class NameValueCollection, that implements an iterator useful to move through the pairs "name=value".
This is the code that solve my problem:
string name;
string value;
HTMLForm form( request );
NameValueCollection::ConstIterator i = form.begin();
while(i!=form.end()){
name=i->first;
value=i->second;
cout << name << "=" << value << endl << flush;
++i;
}
Using poco version 1.11.0-all (2021-06-28)
you can do this:
const Poco::URI Uri(request.getURI());
const Poco::URI::QueryParameters QueryParms = Uri.getQueryParameters();
Poco::URI::QueryParameters is:
std::vector<std::pair<std::string, std::string>>
POCO "NameValueCollection" is almost identical to Vettrasoft Z Directory
namevalue_set_o class, which is documented here:
http://www.vettrasoft.com/man/zman-strings-namevalue_set.html
which at least provides some sample code. The biggest problem I have with
POCO is lack of examples or explanation on how to use it (including the
reference manual pages). For Z Directory's name-value set class, the source code equivalent to that above would look like this:
using namespace std;
int i, ie;
namevalue_set_o nv;
string_o s = "FOO=BAR;DATE=\"12/21/2012\";HOST=vertigo;OSTYPE=\"Windows Vista\"";
nv.load_from_string(s);
i = 0;
while (i < nv.size())
{
const namevalue_pair_o &item = nv.get(i, &ie);
if (!ie)
cout << item.name() << "=" item.value() << endl << flush;
++i;
}

How does one extract the sequence of parsed options using Boost Program Options?

I'm building a graph generator using Boost Graph and Program Options. There are, for example, two types of components C and W, each with 1 source, 1 sink and some additional parameters to specify topology in between. I'd like to be able to stitch them together in the sequence provided by the order of the command line arguments.
For example:
./bin/make_graph -c4,5,1 -w3,3 -c3,1,2
Should create a graph resembling the following:
C -- W -- C
But:
./bin/make_graph -c4,5,1 -c3,1,2 -w3,3
Should create a graph resembling the following:
C -- C -- W
Using boost::program_options, I was unable to determine how to extract the exact order since it "composes" the options of the same string_key into a map with value_type == vector< string > (in my case).
By iterating over the map, the order is lost. Is there a way to not duplicate the parsing, but have a function called (perhaps a callback) every time an option is parsed? I couldn't find documentation in this direction. Any other suggestions?
To convince you that I'm not making this up, here's what I have so far:
namespace bpo = boost::program_options;
std::vector<std::string> args_cat, args_grid, args_web;
bpo::options_description desc("Program options:");
desc.add_options()
.operator ()("help,h","Displays this help message.")
.operator ()("caterpillar,c",bpo::value< std::vector<std::string> >(&args_cat)->default_value( std::vector<std::string>(1,"4,7,2"), "4,7,2" ),"Caterpillar tree with 3 parameters")
.operator ()("grid,g",bpo::value< std::vector<std::string> >(&args_grid)->default_value( std::vector<std::string>(1,"3,4"), "3,4" ),"Rectangular grid with 2 parameters")
.operator ()("web,w",bpo::value< std::vector<std::string> >(&args_web)->default_value( std::vector<std::string>(1,"3,4"), "3,4" ),"Web with 2 parameters")
;
bpo::variables_map ops;
bpo::store(bpo::parse_command_line(argc,argv,desc),ops);
bpo::notify(ops);
if((argc < 2) || (ops.count("help"))) {
std::cout << desc << std::endl;
return;
}
//TODO: remove the following scope block after testing
{
typedef bpo::variables_map::iterator OptionsIterator;
OptionsIterator it = ops.options.begin(), it_end = ops.options.end();
while(it != it_end) {
std::cout << it->first << ": ";
BOOST_FOREACH(std::string value, it->second) {
std::cout << value << " ";
}
std::cout << std::endl;
++it;
}
return;
}
I realize that I could also include the type as a parameter and solve this problem trivially, e.g.:
./bin/make_graph --component c,4,5,1 --component w,3,3 --component c,3,1,2
but that's moving in the direction of writing a parser/validator myself (maybe even without using Boost Program Options):
./bin/make_graph --custom c,4,5,1,w,3,3,c,3,1,2
./bin/make_graph c,4,5,1,w,3,3,c,3,1,2
How would you guys recommend I do this in an elegant way?
Thanks in advance!
PS: I've searched on SO for "[boost] +sequence program options" and "[boost-program-options] +order" (and their variants) before posting this, so I apologize in advance if this turns out to be a duplicate.
Since posting the question, I did some digging and have a "hack" that works with the existing examples I had above.
bpo::parsed_options p_ops = bpo::parse_command_line(argc,argv,desc);
typedef std::vector< bpo::basic_option<char> >::iterator OptionsIterator;
OptionsIterator it = p_ops.options.begin(), it_end = p_ops.options.end();
while(it != it_end) {
std::cout << it->string_key << ": ";
BOOST_FOREACH(std::string value, it->value) {
std::cout << value << " ";
}
std::cout << std::endl;
++it;
}
The reason I call it a hack is because it accesses all arguments as strings, and one would have to extract the types from it much like bpo::variables_map does with the .as<T>() member function. EDIT: It also accesses a member of the options struct directly.
How about this:
./bin/make_graph c,4,5,1 c,3,1,2 w,3,3
Where "c,4,5,1", "c,3,1,2" and "w,3,3" are positional arguments which are stored (in order) in a std::vector<std::string> (just like --input-file in this tutorial) . Then use Boost.Tokenizer or boost::algorithm::split to extract the subtokens from each argument string.
If the graphs can be complex, you should consider making it possible for the user to specify an input file that contains the graph parameters. Boost.Program_Options can parse a user config file that uses the same syntax as the command line options.