I'm fairly new to C++ and am having trouble working with a json file. I am using Xcode (version 6.4). For example, my json file has a similar format to this:
[
{
"assignmentName": "Physics 1",
"dueDate": "2015-10-15T20:11:20Z",
"priority": "High",
},
{
"assignmentName": "Research Paper",
"dueDate": "2015-11-18T00:40:25Z",
"priority": "Low"
}
]
An example of what I am trying to do is write code that looks for information in my json file. If I wanted to print the name of an assignment that is due on November 11, 2015, I would want my output to be "Research Paper".
I've been working on this for the past few days and just keep getting stuck. I've checked out http://www.json.org and looked through the json parsers listed under C++. I've tried to work with them, but either (1) their code is too complicated for me to work with (I don't understand the syntax, even after reading their examples) or (2) I am asked to use other libraries. After looking at every parser underneath the C++ list, json (https://github.com/nlohmann/json) seems like the simplest parser for me to use, but I still feel very lost.
I'm looking for something simple. All I want to do is output the value of whatever variable I'm calling for in my json file (calling for "assignmentName", print "Physics 1")).
From speaking with a friend and vaguely trying to understand the parsers, it seems that in order for me to get the value of some variable in my json file, I need to actually paste the contents of my json file into my Xcode project. Is this true?
If anyone could direct me to a better parser, a better method, or some sort of syntax dictionary for these parsers, it would be highly appreciated!
With the json library you mentioned the relevant code should be
#include "json.hpp"
#include <iostream>
using json = nlohmann::json;
// ... In some method, e.g. main...
std::ifstream file = {"yourfilename.json"};
json obj;
file >> obj;
std::cout << obj[0]["dueDate"]; // Debug output
// End of code
Related
I have a problem while using the jsoncpp lib in a project. I tried to read, edit and write a local json files. The problem i have, is that i can't find a way to get the encoding of writing/reading to UTF-8. It always uses ASCII. This is an Example Json File:
{"Name": "Müller"}
I'm using it like this:
std::ifstream ifs;
std::ofstream ofs;
Json::CharReaderBuilder builder;
Json::StreamWriterBuilder wbuilder;
const std::unique_ptr<Json::StreamWriter> writer(builder.newStreamWriter());
Json::String errs;
parseFromStream(builder, ifs, &root, &errs);
string name = root["Name"].asString();
//if i try to use this data in my wxWidgets Gui now, the Output would be sth like M�ller
root["Straße"] = "Ahornweg 5";
writer->write(root, &ofs);
If i use it like this the jsonfile would look like this:
{ "Stra\u00dfe": "Ahornweg 5", "Name":"M�ller"}
Hope someone can help me. Every help is appreciated.
For anyone in the future wondering, reading this question, it took me some time since I'm a beginner in coding, but it has something to do with the encoding you use in your IDE. Make sure that it's really UTF-8!
I am struggeling to get the response from the server in correct format under Windows. I have tried two C++ libraries Beast, (based on Boost Asio) and Cpr (based on libcurl) and I get the exact same issue with both.
The strange thing is that I also tried this in C# (HttpClient) and everything works just fine. Also, in Postman and other REST tools it looks good.
When I post to the server and should get back the name René I get Ren� instead. Other European characters like æ,ø,å,ö give the same strange output. To me it looks like an issue with utf-8 / iso-8859-1 but I cannot figure it out. The server (based on node.js) and the response is set to push out utf-8. We have tried to just redirect the response so it does not hit a database or anything like that. So, the problem is under C++ it seems. Any suggestions to what I can try would be greatly appreciated.
Example code:
nlohmann::json test_json = nlohmann::json
{
{ "text", "Hi, my name is René" },
{ "language", "en" }
};
auto r = cpr::Post(cpr::Url{ "http://www.exampleserver.com" },
cpr::Body{ test_json.dump() },
cpr::Header{ { "content-type", "application/json; charset=utf-8" } });
std::cout << r.text << std::endl;
It looks like you've got some ISO-8859-1 content being sent through but it's labelled as UTF-8. This causes a whole rash of conversion errors which can mangle non-ASCII characters beyond recognition.
The way to fix this is to either identify the non-UTF-8 data and properly convert it, or identify the payload with the correct MIME type and encoding.
Your issue is with the encoded string. The string is most likely coming back UTF-8 encoded but you are not converting it properly.
There are various libraries that help you convert. It all depends on the version of C++ you're using. Hard to tell you what to use without more details.
I'm trying to parse a JSON file using RapidJSON that has thousands of objects like this one
"Amateur Auteur": {
"layout": "normal",
"name": "Amateur Auteur",
"manaCost": "{1}{W}",
"cmc": 2,
"colors": [
"White"
],
"type": "Creature — Human",
"types": [
"Creature"
],
"subtypes": [
"Human"
],
"text": "Sacrifice Amateur Auteur: Destroy target enchantment.",
"power": "2",
"toughness": "2",
"imageName": "amateur auteur",
"colorIdentity": [
"W"
]
},
I believe I have stored the JSON as a C string correctly I just can't really understand how to use the RapidJSON library to return the values I want from each object.
This is the code for storing the JSON as a C string and then parsing it in case I am doing something incorrect here.
std::ifstream input_file_stream;
input_file_stream.open("AllCards.json", std::ios::binary | std::ios::ate); //Open file in binary mode and seek to end of stream
if (input_file_stream.is_open())
{
std::streampos file_size = input_file_stream.tellg(); //Get position of stream (We do this to get file size since we are at end)
input_file_stream.seekg(0); //Seek back to beginning of stream to start reading
char * bytes = new char[file_size]; //Allocate array to store data in
if (bytes == nullptr)
{
std::cout << "Failed to allocate the char byte block of size: " << file_size << std::endl;
}
input_file_stream.read(bytes, file_size); //read the bytes
document.Parse(bytes);
input_file_stream.close(); //close file since we are done reading bytes
delete[] bytes; //Clean up where we allocated bytes to prevent memory leak
}
else
{
std::cout << "Unable to open file for reading.";
}
Your post seems to ask multiple questions. Lets start from the beginning.
I believe I have stored the JSON as a C string correctly I just can't
really understand how to use the RapidJSON library to return the
values I want from each object.
This is a big no no in software engineering. Never believe or assume. It will come back on release day and haunt you. Instead validate your assertion. Here are a few steps starting from the easy to the more involved.
Print out your C-String.
The easiest way to confirm the content of your variable, especially string data, is to simply print to the screen. Nothing easier then seeing your JSON data print to the screen to confirm you have read it in correctly.
std::cout.write(bytes, filesize);
Breakpoint / Debugger
If you have some reason for not printing out your variable, then compile your code with debugging enabled and load in GDB if you're using g++, lldb if you're using clang++, or simply place a breakpoint in visual studio if you're using an VS or VSCode. Once at the breakpoint you can inspect the content of your variable.
However, before we move on I wouldn't be helping you if I didn't point out that reading files in CPP is much much easier then the way you're reading.
// Open File
std::ifstream in("AllCards.json", std::ios::binary);
if(!in)
throw std::runtime_error("Failed to open file.");
// dont skip on whitespace
std::noskipws(in);
// Read in content
std::istreambuf_iterator<char> head(in);
std::istreambuf_iterator<char> tail;
std::string data(head, tail);
At the end of the above code you now have all of the content from your file read into an std::string which wraps a null terminated or C-String. You can access that data by calling .c_str() on your string instances. If you do it this way you no longer have to worry about calling new or delete[] as the std::string class takes care of the buffer for you. Just make sure it hangs around as long as you're using it in RapidJSON.
This is the code for storing the JSON as a C string and then parsing
it in case I am doing something incorrect here.
No. according the the rapid JSON documentation you create a document object and have it parse the string.
Document d;
d.Parse(data.c_str());
However, that just creates the element for querying the document. You can ask the document if specific items exist (d.hasMember("")), ask for a string typed members content d["name"].GetString() or anything listed over at the documentation. You can read the tutorial here.
By the way. Welcome to SO. I would suggest that next time you post ask a more targeted question. What exactly are you trying to do with the parsed JSON element?
I just can't really understand how to use the RapidJSON library to
return the values I want from each object.
I cannot answer this question for two reasons. What are you trying to extract? What have you tried? Have you read the documentation and do not understand a specific item?
Here is a good place to read up on asking better questions. Please don't think I am coming down on you. I bring this up because asking better questions will get you better and more specific answers. poorly asked questions always run the risk of being ignored or, dare I say it, the good old does google not work today response.
https://stackoverflow.com/help/how-to-ask
** Updates **
To your question. You can iterate over all objects.
using namespace rapidjson;
Document d;
d.Parse(data.c_str());
for (auto itr = d.MemberBegin(); itr != d.MemberEnd(); ++itr){
std::cout << itr->name.GetString() << '\n';
}
I want to load multiple file names from a dictionary(for example "Data/lua_files") into a C++ string, without adding them manually. For example whenever I add a new file I should be able to use it when I start my program without adding any code. Currently I use Sol2.0.
Can I save all file names into a new .txt file?(with a lua script?)
Is there any way to archive that?
I checked Google but didnt find anything
Thanks!
If by a "dictionary" you mean a Lua table:
fileNames = {
"file1.txt",
"file2.txt",
"file3.txt"
}
Then it's as easy as table.concat(fileNames, ","). It will return a string which you can then e.g. save into a global variable:
fileNamesString = table.concat(fileNames, ",")
And then use Sol to read it from the C++ side. I wonder if it's necessary to go through that extra step, though; I thought that the library supported direct table access. With that in mind, it'd be enough to just:
sol::lua_state lua;
// read your script file here
for (std::string const& fileName : lua["fileNames"]) {
// do your operation
}
I'm a newbie looking for a fast and easy way to parse a text file in C or C++ (wxWidgets)
The file will look something like this (A main category with "sub-objects") which will appear in a list box
[CategoryA]
[SubCat]
Str1 = Test
Str2 = Description
[SubCat] [End]
[SubCat]
Str1 = Othertest
...
[CategoryA] [End]
Any suggestions?
Sounds like you want to parse a file that's pretty close to an ini file.
There's at least a few INI parser libraries out there: minIni, iniParser, libini, for instance.
It should be fairly easy to write your own parser for this if you use streams. You can read a file using an std::ifstream:
std::ifstream ifs("filename.ext");
if(!ifs.good()) throw my_exceptions("cannot open file");
read_file(ifs);
Since it seems line-oriented, you would then first read lines, and then process these:
void read_file(std::istream& is)
{
for(;;) {
std::string line;
std::getline(is, line);
if(!is) break;
std::istringstream iss(line);
// read from iss
}
if(!is.eof()) throw my_exceptions("error reading file");
}
For the actual parsing, you could 1) first peek at the first character. If that's a [, pop it from the stream, and use std::getline(is,identifier,']') to read whatever is within '[' and ']'. If it isn't a [, use std::getline(is, key, '=') to read the left side of a key-value pair, and then std::getline(is, value) to read the right side.
Note: Stream input, unfortunately, is usually not exactly lightning fast. (This doesn't have to be that way, but in practice this often is.) However, it is really easy to do and it is fairly easy to do it right, once you know a very few patterns to work with its peculiarities (like if(strm.good()) not being the same as if(strm) and not being the opposite of if(strm.bad()) and a few other things you'll have to get used to). For something as performance-critical (har har!) as reading an ini file from disk, it should be fast enough in 999,999 out of 1,000,000 cases.
You may want to try Boost.Program_Options. However it has slightly different formatting. More close to INI files. Subcategories are done like this:
[CategoryA]
Option = Data
[CategoryB.Subcategory1]
Option = Data
[CategoryB.Subcategory2]
Option = Data
Also it has some other features so it is actually very useful IMO.
Try Configurator. It's easy-to-use and flexible C++ library for configuration file parsing (from simplest INI to complex files with arbitrary nesting and semantic checking). Header-only and cross-platform. Uses Boost C++ libraries.
See: http://opensource.dshevchenko.biz/configurator
It looks more straightforward to implement your own parser than to try to adapt an existing one you are unfamiliar with.
Your structure seems - from your example - to be line-based. This makes parsing it easy.
It generally makes sense to load your file into a tree, and then walk around it as necessary.
On Windows only, GetPrivateProfileSection does this. It's deprecated in favor of the registry but it's still here and it still works.
How about trying to make a simple XML file? There are plenty of libraries that can help you read it, and the added bonus is that a lot of other programs/languages can read it too.
If you're using wxWidgets I would consider wxFileConfig. I'm not using wxWidgets, but the class seems to support categories with sub-categories.
When you are using GTK, you are lucky.
You can use the Glib KeyFile save_to_file and load_from_file.
https://docs.gtk.org/glib/struct.KeyFile.html
Or when using Gtkmm (C++).
See: https://developer-old.gnome.org/glibmm/stable/classGlib_1_1KeyFile.html
Example in C++ with load_from_file:
#include <glibmm.h>
#include <string>
Glib::KeyFile keyfile;
keyfile.load_from_file(file_path);
std::string path = keyfile.get_string("General", "Path");
bool is_enabled = keyfile.get_boolean("General", "IsEnabled");
Saving is as easy as calling save_to_file:
Glib::KeyFile keyfile;
keyfile.set_string("General", "Path", path);
keyfile.set_boolean("General", "IsEnabled", is_enabled);
keyfile.save_to_file(file_path);