How to validate json file in C++? - c++

Currently I have a JSON file (Test.json) with some valid data in json format and i'am trying to parse the data using a json object (readData) using JsonCpp, as below
Json::Value readData;
std::ifstream readFile("Test.json");
readFile >> readData;
This works fine if the json file is having valid contents in json format, but it crashes at "readFile >> readData" if the contents are not of a json format.
is there any way to validate the file before assigning it to a json object in C++ ?

It doesn't "crash"; it throws an exception. I'd guess you're not catching that exception.
Anyway, for reading JSON you probably want to use parseFromStream. This allows you to set options, and returns true or false. The operator>> you're using is just a shortcut.
Please read documentation for the functions that you use. It's there to help you.

I think you should use this code instead. It will not crash the program. It will just show you (on STDERR) what error has occurred.
Json::Value readData;
std::ifstream readFile("Test.json");
if (readFile) {
try {
readFile >> readData;
} catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
}
}
else {
std::cerr << "File not found!" << std::endl;
}
According to the documentation of JsonCpp, operator>>() results in std::exception on parse error.

Related

C++ Write file to C drive

My program lets the user specify where to write a file. If they specify "C:\output.txt" the file operations seem to succeed (no errors) however the file doesn't get created. I know this is due to Windows requiring elevated permission and when running as administrator it does write to C:\. My question is how can I detect that the file didn't actually open?
This block "succeeds" without error even though the file doesn't actually get created:
ofstream ofs;
try {
ofs.open(outputFile);
if (!ofs.is_open()){
throw "The file could not be opened";
}
ofs << "it worked";
ofs.close();
} catch (const char* ex){
cout << ex;
return 1;
}
If open() fails to create/open a file, the stream's is_open() method will return false, and the stream's failbit state flag will be set, so the fail() method and operator! will return true:
ofs.open(outputFile);
if (!ofs.is_open())
// or: if (ofs.fail())
// or: if (!ofs)
{
cout << "The file could not be opened";
return 1;
}
If no failure is reported, then no failure occurred. The file was created somewhere, but that might not be where you are expecting.
If you open a file using a relative path, then it is relative to the calling process's current working directory, which may be different than you are expecting. So always use absolute paths.
If you try to create a file and you don't have access to the folder where you are creating the file, the file creation might get transparently redirected to a VirtualStore folder within the user's profile instead.
Try using SysInternals Process Monitor to see where exactly the file is being created (or even, where open() is attempting to create the file, if no access is allowed and redirection doesn't happen).
On a side note, what you showed is a misuse of exception handling. You don't really need an exception at all, as shown above. However, if you do want to throw an exception on failure, consider using the ofstream::exceptions() method instead:
ofstream ofs;
ofs.exceptions(ofstream::failbit);
try {
ofs.open(outputFile);
ofs << "it worked";
}
catch (std::ios_base::failure &) {
cout << "The file could not be opened, or written to";
return 1;
}
ofs.close();

CPPRestSDK (casablanca) Extract JSON From Incoming WebSocket Messages (malformed token)

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?
wsClient.set_message_handler([=](websocket_incoming_message msg)
{
// handle message from server...
printf("[WebSocket INBOUND]: %s", msg.extract_string().get().c_str());
printJSON(json::value::parse(conversions::to_string_t(msg.extract_string().get())));
});
printJSON runs through the json::value and prints each key-value-pair.
Unhandled exception at 0x00007FF866923FB8 in RestAPI.exe: Microsoft
C++ exception: web::json::json_exception at memory location
0x0000003E553FDDC0. occurred
Console Output:
[WebSocket INBOUND]:
{"t":null,"s":null,"op":10,"d":{"heartbeat_interval":41250,"_trace":["gateway-prd-main-cr3x"]}}
Even though we can compile and run the application, I figure the exception is being caused due to the fact that were passing a string containing a JSON Table and not a single element? Does this mean I need to manually parse the string and pull out each key-value-pair while simultaneously building the json array?
There must be a way to do this, it seems like basic needed functionality..
A similar unresolved question
Any help here would be greatly appreciated!
Thank you for your time.
Try catching web::json::json_exception and print the message, it may give you a hint about what's wrong
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("users_list")) {
std::cout << "\n" << "users list " << 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)

getline() throws basic_ios::clear exception after reading the last line

I am writing a unit test for file read using qtestlib, C++ (clang LLVM version 8.0). I have the following code for reading a file line by line.
std::ifstream infile;
try {
infile.open(path.c_str());
std::ios_base::iostate exceptionMask = infile.exceptions() | std::ios::failbit;
infile.exceptions(exceptionMask);
} catch (std::ios_base::failure& e) {
// print the exception
qDebug() << "Exception caught: " << QString::fromStdString(e.what());
}
try {
std::string line;
while (std::getline(infile, line)) {
// print the line
qDebug() << QString::fromStdString(line);
}
} catch (std::ios_base::failure& e) {
qDebug() << "Exception caught: " << QString::fromStdString(e.what());
}
The issue:
The above code reads all the lines in the file and prints it. But after printing the last line, it throws an exception and prints the following,
Exception caught: "basic_ios::clear"
I followed many threads, but could not find the solution to this. Why am I getting this error?
After you have read and printed all the lines, the while (std::getline(infile, line)) will still try to read another line. If it fails totally - zero characters read - it sets failbit to signal its failure.
The odd part of the error message is that, despite its name, basic_ios::clear can be used to set the failure bit and will also throw an exception if you have enabled the same bit with exceptions.
Take a look on documentation of std::getline. Scetion about setting flags:
failbit
The input obtained could not be interpreted as a valid textual representation of an object of this type. In this case,
distr preserves the parameters and internal data it had before the call. Notice that some eofbit cases will also set failbit.
The last sentence is a bit fuzzy, but can explain observed behavior.
I did some experiments and found the pattern. First I've corrected your code this way:
try {
std::string line;
while (std::getline(infile, line)) {
// print the line
qDebug() << QString::fromStdString(line);
if (infile.eof()) {
return;
}
}
} catch (std::ios_base::failure& e) {
qDebug() << "Exception caught: " << QString::fromStdString(e.what());
}
Now if input file ends with empty line I get an exception, if last line doesn't end with "\n" return breaks a loop.
So falbit is set if you are trying to read stream which already reached end of stream.
Without "if" check you are always doing this reading and always get an exception.
For last line empty I have some clues, but have not idea how to explain it nicely. First have to check behavior of other platforms/compilers.

ifstream.open() not opening file

I've been having a nightmare this evening trying to get some very simple I/O functionality going. As embarrassing as it is, I've had some great help from people on here!
My current issue is that I'm attempting to use ifstream.open() and it simply is not opening the file. This is confirmed by getline(ifstream,line); returning false on it's first call.
Here is a copy paste of the current code:
std::string FSXController::readLine(int offset, FileLookupFlag flag)
{
// Storage Buffer
string line;
streampos sPos(offset);
try
{
// Init stream
if (!m_ifs.is_open())
m_ifs.open("C:\\Users\\guyth\\Documents\\test.txt", fstream::in);
}
catch (int errorCode)
{
showException(errorCode);
return "";
}
// Set stream to read input line
m_ifs.seekg(sPos);
if (!getline(m_ifs, line))
return "";
// Close stream if no multiple selection required
if (flag == FileLookupFlag::single)
m_ifs.close();
return line;
}
This code is in 'bug fix mode' and so therefore is pretty messy, don't worry too much about that, cleanup will happen when this method is finally working.
I have tried:
Absolute file path
Saving path into string and then calling the .c_str() method.
Running VS 2015 in Administrator mode
Ensuring file has read/wright access
Ensuring no duplicate file extensions
Yes the file definitely has content! :D
I'm kinda out of ideas now and am really not sure why this file is refusing to load.
The condition: if (!getline(m_ifs, line)) Repeatedly returns true... :(
EDIT: I've just tried checking m_ifs.fail() immediately after the open and it returns true, so we know the fail flag was triggered :/
Thanks
Guy
Enable exceptions before opening the stream:
m_ifs.exceptions ( std::ifstream::failbit | std::ifstream::badbit );
Otherwise m_ifs.open won't throw.
And you have to catch std::ifstream::failure:
try {
m_ifs.open("C:\\Users\\guyth\\Documents\\test.txt", fstream::in);
}
catch (std::ifstream::failure e) {
std::cerr << "Exception opening file: " << std::strerror(errno) << "\n";
}
See ios::exceptions for more details.

Error checking C++ streams with Exceptions

I have a class X which has a writeBinary(ostream) method which can throw a custom exception
if something happens to the stream.
What is the correct way to write to fstream and error check?
Here is my version: I would like to know if I am missing something or I need to catch
errors.
ofstream ofs("X.binary.tmp");
if (!ofs) {
cerr << "Could not open file for writing";
throw runtime_error("Could not open file for writing");
}
try {
x.writeBinary(ofs);
} catch(CustomException& e) {
// remove the temporary file
int x = unlink("X.binary.tmp");
if (x) {
cerr << "Failed to remove file";
}
throw;
}
if (!ofs) { // is this check necessary?
int x = unlink("X.binary.tmp"):
if (x) {
cerr << "Failed to remove file";
}
throw std::runtime_error("Stream error");
}
rename("X.binary.tmp", "X.binary");
Can this hodgepodge mess of exceptions be simplified?
One way to make things a lot less messy is to create a resource handling object - so a "tempfile" object, which opens a new file by the name given to the constructor, and in the destructor calls unlink if the filename isn't blank. Then have it have a rename function that renames the file and sets the name to blank, for example. And of course a function that gives you the ofstream from the object.
I'm sure it's as much code, but it'll look a lot cleaner, and it will make life much easier.
Writing code that copes with lots of potential errors is not easy, as you may have noticed...