I finally found a path file QFile would accept using QFile.exist() and a healthy dose of trial and error.
I want to know why the following works:
#include <QFile>
#include <QByteArray>
#include <QJsonObject>
#include <QJsonDocument>
QString path = QDir::currentPath(); // Get current dir
path.append("/noteLibrary.json");
QFile file(path); // Give QFile current dir + path to file
if (!file.exists()) { // Check to see if QFile found the file at given file_path
qDebug() << "NO FILE HERE";
}
qDebug() << path; // See what path was finally successful
file.open(QIODevice::ReadOnly); // Continue parsing document to confirm everything else is functioning normally.
QByteArray rawData = file.readAll();
// Parse document
QJsonDocument doc(QJsonDocument::fromJson(rawData));
// Get JSON object
QJsonObject json = doc.object();
// Access properties
qDebug() << json["die"].toString(); // Should output "280C4"
Successful output:
"/home/pi/noteLibrary.json"
"280C4"
But the following does NOT work:
#include <QFile>
#include <QByteArray>
#include <QJsonObject>
#include <QJsonDocument>
QFile file("/home/pi/noteLibrary.json"); // Give QFile current dir + path to file
if (!file.exists()) { // Check to see if QFile found the file at given file_path
qDebug() << "NO FILE HERE";
}
//qDebug() << path; // See what path was finally successful
file.open(QIODevice::ReadOnly); // Continue parsing document to confirm everything else is functioning normally.
QByteArray rawData = file.readAll();
// Parse document
QJsonDocument doc(QJsonDocument::fromJson(rawData));
// Get JSON object
QJsonObject json = doc.object();
// Access properties
qDebug() << json["die"].toString(); // Should output "280C4"
Error output:
NO FILE HERE
QIODevice::read (QFile, "/home/pi/Desktop/noteLibrary.json"): device not open
""
Why would QFile treat these differently? Is this a QString format issue? Or is the fact that I'm deploying this remotely to a Raspberry Pi 3 possibly to blame?
Regardless of what was wrong with my code above, with the below code giving QFile the absolute path does work equally to creating a QString with currentPath(). I must have had something else wrong, my mistake!
noteLibrary.json
{"note": [{
"profile": "C4",
"die": "280C4",
"pressure": 800,
"position": 10000
},
{
"profile": "CC4",
"die": "2280C4",
"pressure": 8800,
"position": 110000
}
],
"test": {
"profile": "CCC4",
"die": "22280C4",
"pressure": 88800,
"position": 1110000
}
}
main.cpp excerpt
QFile file("/home/pi/noteLibrary.json");
if (!file.exists()) qDebug() << "NO FILE FOUND";
file.open(QIODevice::ReadOnly);
QByteArray rawData = file.readAll();
QJsonDocument doc(QJsonDocument::fromJson(rawData)); // Parse document
QJsonObject jObj = doc.object(); // Get JSON object
qDebug() << jObj["test"];
Application Output
QJsonValue(object,QJsonObject({"die":"22280C4","position":1110000,"pressure":88800,"profile":"CCC4"}))
Seems odd that it displays property values in alphabetical order, not the order listed in the document.
Related
I'm currently working in C++, getting an HTTP response from a request that I write into a .txt file using ostream. This happens asynchronously and I don't want to change this.
Once the data is done being written, I want to read from the file
{"data":{"request":[{"type":"City","query":"London, United Kingdom"}],"weather":[{"date":"2013-04-21","astronomy".....
~somehow~ prettify the string using either an outside library like nlohmann/json or other(?) and then
a)print it to the console and
b) save it in a different file (pretty.json)
I am having trouble understanding which method to use from:
https://github.com/nlohmann/json
Any ideas how to approach this?
I was thinking getting the file line by line until I hit EOF into a sort of "buffer" and then running _json on that and saving the solution which can be displayed on the console...
My code so far
#include <cpprest/http_client.h>
#include <cpprest/filestream.h>
#include <iostream>
#include <sstream>
#include "json.hpp"
using namespace utility; // string conversion
using namespace web; // URI
using namespace web::http; // HTTP commands
using namespace web::http::client; // HTTP Client features
using namespace concurrency::streams; // Asynch streams, like Node
using json = nlohmann::json;
int main()
{
auto fileStream = std::make_shared<ostream>();
// Open stream to output file.
pplx::task<void> requestTask = fstream::open_ostream(U("results.txt"))
.then([=](ostream outFile)
{
*fileStream = outFile;
http_client client //gets the info
return client.request(methods::GET, stringBuilder.to_string());
})
.then([=](http_response response) // set up response handler
{
printf("Received response status code:%u\n", response.status_code());
return response.body().read_to_end(fileStream->streambuf());
})
.then([=](size_t) // close file stream
{
return fileStream->close();
})
.then([=]()
{
nlohmann::json j;
std::ifstream i;
i.open("results.txt"); // ?? <<< === this is where my question is
});
// Wait for all the outstanding I/O to complete, handle exceptions
try
{
requestTask.wait();
}
catch (const std::exception &e)
{
printf("Error exception:%s\n", e.what());
}
return 0;
}
SOLUTION:
.then([=]()
{
// read a JSON file
std::ifstream readFromFile("results.txt");
if (readFromFile.is_open()) {
nlohmann::json j;
readFromFile >> j;
// write prettified JSON to another file
std::ofstream writeToFile("pretty.json");
writeToFile << std::setw(4) << j << std::endl;
readFromFile.close();
writeToFile.close();
}
else {
std::cout << "unable to open file";
}
});
You have two choices to prettify with nlohmann.
Uses dump which produces a string
int indent = 4;
nlohmann::json data;
data.dump(indent);
Or use the stream output overload with field width set
std::ofstream o("pretty.json");
o << std::setw(4) << data << std::endl;
I am having problems saving the JSON response using json cpprestsdk so that I can pass it back to the calling function. The parse() function seems to do a shallow copy rather than a deep copy. Can someone please help me with the method that I should be using to make a deep copy of the results returned?
Thanks in advance.
functionA()
{
web::json::value myJsonObjSv;
http_client client(L"http://url.com:8080/getdata");
return client.request(methods::GET).then([](http_response response) -> pplx::task<json::value> {
if(response.status_code() == status_codes::OK) {
return response.extract_json();
}
return pplx::task_from_result(json::value()); }).then([&myJsonObjSv](pplx::task<json::value> previousTask) {
try {
// capture json response to json_resp
json::value const resp = previousTask.get();
// Now save the JSON object to return from this function
myJsonObjSv = json::value::parse(resp.serialize());
// This statement displays the contents of myJsonObjSv to be the same as resp
wcout << "myJsonObjSv[" myJsonObjSv.serialize() << "]" << endl;
}
catch( const http_exception& e) {
// print error
}
});
// This statement to display the contents of myJsonObjSv crashes since there was a shallow copy made rather than a deep copy
wcout << "myJsonObjSv[" myJsonObjSv.serialize() << "]" << endl;
}
My JSON is this:
{
"apps":[
{
"id":"x",
"val":"y",
}
]
}
I can get id's value "x" by looping, and exiting when it.first is id:
for (const ptree::value_type &app : root.get_child("apps"))
{
for (const ptree::value_type &it : app.second) {
if (it.first == "id") {
std::cout << it.second.get_value<std::string>().c_str() << std::endl;
}
}
}
What I want, however, is to get the id's value by something like this:
std::cout << root.get<std::string>("apps[0].id").c_str() << std::endl;
Of course this displays nothing to me, for I am probably using wrong syntax accessing 1st element of the apps array. It might be that this has to be done in a different way all together.
I have found only this ugly and dangerous method:
std::cout << root.get_child("apps").begin()->second.begin()->second.get_value<std::string>().c_str() << std::endl;
I cannot really use it this way as it won't throw an exception when the array is empty, it will core dump!
Below is the whole program to make it easier for any one who wants to help:
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
using boost::property_tree::ptree;
int main()
{
std::stringstream ss("{\"apps\":[{\"id\":\"x\",\"val\":\"y\"}]}");
ptree root;
read_json(ss, root);
for (const ptree::value_type &app : root.get_child("apps"))
{
for (const ptree::value_type &it : app.second) {
if (it.first == "id") {
std::cout << it.second.get_value<std::string>().c_str() << std::endl;
}
}
}
std::cout << root.get_child("apps").begin()->second.begin()->second.get_value<std::string>().c_str() << std::endl;
return 0;
}
As the docs say, array elements are nodes with "" keys.
If you're after the first element, you're in luck:
root.get("apps..id", "")
The .. in the path selects the first empty key
Live On Coliru
#include <boost/property_tree/json_parser.hpp>
#include <iostream>
using boost::property_tree::ptree;
int main() {
std::stringstream ss(R"({"apps":[{"id":"x","val":"y"}]})");
ptree root;
read_json(ss, root);
std::cout << root.get("apps..id", "") << "\n";
}
BONUS
If you need to address elements other than the first, write a helper function. This would be a good start:
#include <string>
#include <stdexcept> // std::out_of_range
template <typename Tree>
Tree query(Tree& pt, typename Tree::path_type path) {
if (path.empty())
return pt;
auto const head = path.reduce();
auto subscript = head.find('[');
auto name = head.substr(0, subscript);
auto index = std::string::npos != subscript && head.back() == ']'
? std::stoul(head.substr(subscript+1))
: 0u;
auto matches = pt.equal_range(name);
if (matches.first==matches.second)
throw std::out_of_range("name:" + name);
for (; matches.first != matches.second && index; --index)
++matches.first;
if (index || matches.first==matches.second)
throw std::out_of_range("index:" + head);
return query(matches.first->second, path);
}
Here's some live tests using it:
Live On Coliru
#include <boost/property_tree/json_parser.hpp>
#include <iostream>
using boost::property_tree::ptree;
int main() {
std::stringstream ss(R"({
"apps": [
{
"id": "x",
"val": "y",
"id": "hidden duplicate"
},
{
"id": "a",
"val": "b"
}
]
})");
ptree root;
read_json(ss, root);
for (auto path : {
"apps..id", "apps.[0].id", // (equivalent)
//
"apps.[0].id[]", // invalid
"apps.[0].id[0]", // x
"apps.[0].id[1]", // hidden duplicate
"apps.[1].id", // a
"apps.[1].id[0]", // a
"apps.[1].id[1]", // out of range
"apps.[2].id", // out of range
"drinks" // huh, no drinks at the foo bar
}) try {
std::cout << "Path '" << path << "' -> ";
std::cout << query(root, path).get_value<std::string>() << "\n";
} catch(std::exception const& e) {
std::cout << "Error: " << e.what() << "\n";
}
}
Prints:
Path 'apps..id' -> x
Path 'apps.[0].id' -> x
Path 'apps.[0].id[]' -> Error: stoul
Path 'apps.[0].id[0]' -> x
Path 'apps.[0].id[1]' -> hidden duplicate
Path 'apps.[1].id' -> a
Path 'apps.[1].id[0]' -> a
Path 'apps.[1].id[1]' -> Error: index:id[1]
Path 'apps.[2].id' -> Error: index:[2]
Path 'drinks' -> Error: name:drinks
{
"Restricted_parameters":
{
"abcd"
"efgh"
"ijkl"
"mnop"
}
}
I am new about json files and parsing it and in my current college project when I am rying to parse the json file it is giving error
Can anyone please let me know how to parse above json file
I am using JSON parser APIs also
Jason_parser_edf::Jason_parser_edf()
{
Json_parser file_parser;
// Create empty property tree object
using boost::property_tree::ptree;
ptree pt;
uint32_t nb = 0;
std::string // const std::string restricted_parameters = "Restricted_parameters";
file_parser.open_json_file(current_file_path, &pt);
ptree::const_iterator end = pt.end();
for (ptree::const_iterator it = pt.begin(); it != end; ++it)
{
BOOST_FOREACH( ptree::value_type const& v, pt.get_child(it->first) ) // parasoft-suppress MISRA2008-6_4_1 "BOOST library" // parasoft-suppress NAMING-33 "BOOST library" // parasoft-suppress BD-PB-CC "BOOST library" // parasoft-suppress MISRA2008-6_3_1 "BOOST library"
{
// Getting additional fields
std::string additional_field_name = v.second.get<std::string>("Restricted_parameters");
cout << additional_field_name << endl;
}
}
// second way I am trying
for (auto & array_element: pt) {
for (auto & property: array_element.second) {
std::cout << property.first << endl;
}
}
// db_conf_structure.dump();
}
This is not a valid format of JSON, you must remember always to have pair key: value
{
"Restricted_parameters":
{
"abcd": "val1",
"efgh": "val2",
"ijkl": "val3",
"mnop": "val4"
}
}
or use array
{
"Restricted_parameters":
[
"abcd",
"efgh",
"ijkl",
"mnop"
]
}
The JSON is invalid. An array is initialized through square brackets and you need commas between the items.
{
"Restricted_parameters":
[
"abcd",
"efgh",
"ijkl",
"mnop"
]
}
Suppose I have a double Eigen matrix and I want to write it to a csv file. I find the way of writing into a file in raw format but I need commas between entries. Here is the code I foudn for simple writing.
void writeToCSVfile(string name, MatrixXd matrix)
{
ofstream file(name.c_str());
if (file.is_open())
{
file << matrix << '\n';
//file << "m" << '\n' << colm(matrix) << '\n';
}
}
Using format is a bit more concise:
// define the format you want, you only need one instance of this...
const static IOFormat CSVFormat(StreamPrecision, DontAlignCols, ", ", "\n");
...
void writeToCSVfile(string name, MatrixXd matrix)
{
ofstream file(name.c_str());
file << matrix.format(CSVFormat);
}
Here is what I came up;
void writeToCSVfile(string name, MatrixXd matrix)
{
ofstream file(name.c_str());
for(int i = 0; i < matrix.rows(); i++){
for(int j = 0; j < matrix.cols(); j++){
string str = lexical_cast<std::string>(matrix(i,j));
if(j+1 == matrix.cols()){
file<<str;
}else{
file<<str<<',';
}
}
file<<'\n';
}
}
This is the MWE to the solution given by Partha Lal.
// eigen2csv.cpp
#include <Eigen/Dense>
#include <iostream>
#include <fstream>
// define the format you want, you only need one instance of this...
// see https://eigen.tuxfamily.org/dox/structEigen_1_1IOFormat.html
const static Eigen::IOFormat CSVFormat(Eigen::StreamPrecision, Eigen::DontAlignCols, ", ", "\n");
// writing functions taking Eigen types as parameters,
// see https://eigen.tuxfamily.org/dox/TopicFunctionTakingEigenTypes.html
template <typename Derived>
void writeToCSVfile(std::string name, const Eigen::MatrixBase<Derived>& matrix)
{
std::ofstream file(name.c_str());
file << matrix.format(CSVFormat);
// file.close() is not necessary,
// desctructur closes file, see https://en.cppreference.com/w/cpp/io/basic_ofstream
}
int main()
{
Eigen::MatrixXd vals = Eigen::MatrixXd::Random(10, 3);
writeToCSVfile("test.csv", vals);
}
Compile with g++ eigen2csv.cpp -I<EigenIncludePath>.