• Advertisement
Sign in to follow this  

json c++

This topic is 1234 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hey all, quick question:

I'm using jsoncpp to try to load from a json file, and despite trying to read the documentation (I'm still rather new, and sometimes documentation can go over my head), I'm a little confused still on some details.

I've gotten a bare bones test working:

std::string json_example = "{\"array\":\
                            [\"item1\",\
                            \"item2\"], \
                            \"not an array\": \
                            \"asdf\" \
    }";

    Json::Value root;
    Json::Reader reader;
    bool parsedSuccess = reader.parse(json_example, root, false);

    if(not parsedSuccess)
    {
        std::cout<<"whoops"<<std::endl;
    }

    const Json::Value array = root["array"];
    for(unsigned int index = 0; index<array.size(); ++index)
    {
        std::cout<<"Element "<<index<<" in array: "<<array[index].asString()<<std::endl;
    }

This seems to work. However, it's only reading from a string created in the code. Is there supposed to be a way to load directly from a file in the directory? I was about to try to just load a string from a file using fstream and then parse it, but I thought there's perhaps a cleaner way (hopefully within the jsoncpp code) to do this.

 

Anyhow, any recommendations here would be greatly appreciated. This is my first foray into reading/writing to files (I've been procrastinating learning it). I'm mostly wanting to read/write things like game options, character data, save states etc for the game. Feel free to let me know if I'm going about this the wrong way, or point me to a decent tutorial. There seems to be a lot of options out there, but little in the way of clear tutorials. I toyed with XML for a little bit before settling on json. But, I'm not rigidly set on it. 

 

Thanks in advance for any input.

 

Cheers :)

Share this post


Link to post
Share on other sites
Advertisement
I had a quick flip through the documentation and there appears to be an overload of Json::Reader::parse (which you are already using) which takes an std::istream as input. Doesn't that solve your problem right away?

Share this post


Link to post
Share on other sites

... However, it's only reading from a string created in the code. Is there supposed to be a way to load directly from a file in the directory? I was about to try to just load a string from a file using fstream and then parse it, but I thought there's perhaps a cleaner way (hopefully within the jsoncpp code) to do this.

Directly loading files is not necessarily the "cleaner way". It requires dealing with the differences in file path syntaxes (because many libraries are multi-platform), and it requires the source to be available as a stand-alone file, of course. Think of reading from a network source, or reading of file fragments embedded in an own file format. This is not that unusual. Games often use package files to reduce the amount of single files for performance and/or maintenance purposes. Or the embedding of preview images or the color profiles in e.g. PSD files. The library should also work if the stuff is already in memory (perhaps already loaded as a blob from a package file, or received from a network socket). Maybe it should also work with fragments only (similar to XML fragments) although encoding information are not available from the fragment any more.

 

IMHO, the cleaner way is to give a library a generic way for reading content, and do eventual file handling (locating, opening, closing) and loading externally to that.

Edited by haegarr

Share this post


Link to post
Share on other sites

I had a quick flip through the documentation and there appears to be an overload of Json::Reader::parse (which you are already using) which takes an std::istream as input. Doesn't that solve your problem right away?

I saw that in the documentation. So, in addition to haegarr's comment, I'm gathering that the correct (or preferred, or easiest, what-have-you) way to go about this to load the file independently, then parse it with jsoncpp. is that right? For whatever reason (probably because I'm new tongue.png), I was under the impression that the parser would read directly from the file.

 

 

 


... However, it's only reading from a string created in the code. Is there supposed to be a way to load directly from a file in the directory? I was about to try to just load a string from a file using fstream and then parse it, but I thought there's perhaps a cleaner way (hopefully within the jsoncpp code) to do this.

Directly loading files is not necessarily the "cleaner way". It requires dealing with the differences in file path syntaxes (because many libraries are multi-platform), and it requires the source to be available as a stand-alone file, of course. Think of reading from a network source, or reading of file fragments embedded in an own file format. This is not that unusual. Games often use package files to reduce the amount of single files for performance and/or maintenance purposes. Or the embedding of preview images or the color profiles in e.g. PSD files. The library should also work if the stuff is already in memory (perhaps already loaded as a blob from a package file, or received from a network socket). Maybe it should also work with fragments only (similar to XML fragments) although encoding information are not available from the fragment any more.

 

IMHO, the cleaner way is to give a library a generic way for reading content, and do eventual file handling (locating, opening, closing) and loading externally to that.

 

This actually does make sense to me and in hindsight and I can see why the library is much more versatile focusing solely on parsing the information and not handling the loading of the files. Thank you for clearing that up. 

 

Alright, that clears things up considerably for me. I have a bit of reading to do in c++/iostream/ifstream(again, I had mostly been avoiding learning to read from files. I was a bit intimidated by it, and hadn't had much of a use until now for it), and a lot of work figuring out to incorporate it into my program.

 

Thanks for the input, all. smile.png

Edited by Misantes

Share this post


Link to post
Share on other sites
You do not have to load the content yourself. You can create an std::ifstream, open the file and then pass the stream to Json::Reader::parse.

You can also wrap a block of memory into an std::istringstream (or a custom stream) and pass it to Json::Reader::parse.

You can also create a custom stream which can load directly from a network source or your custom package format and pass it directly to Json::Reader::parse.

That's the nice thing about abstraction. A stream is just a source of data until it says it has no more data. Whether the data is coming from a file, the network or some other arcane source is irrelevant for the user of the stream.

Share this post


Link to post
Share on other sites

You do not have to load the content yourself. You can create an std::ifstream, open the file and then pass the stream to Json::Reader::parse.

You can also wrap a block of memory into an std::istringstream (or a custom stream) and pass it to Json::Reader::parse.

You can also create a custom stream which can load directly from a network source or your custom package format and pass it directly to Json::Reader::parse.

That's the nice thing about abstraction. A stream is just a source of data until it says it has no more data. Whether the data is coming from a file, the network or some other arcane source is irrelevant for the user of the stream.

ah, brilliant. I wasn't aware of that. Again, I'm just starting to research reading/writing files, but up until now haven't really explored the ins/outs of iostream past the standard cout/cin, and some limited fstream uses. I have some good resources to look through for the rest of the week smile.png.

 

I'm sure I'll have some follow-up questions down the line tongue.png but, I think I need to dig into things a little (lot) before I have anything really constructive to ask.

Edited by Misantes

Share this post


Link to post
Share on other sites


That's the nice thing about abstraction. A stream is just a source of data until it says it has no more data. Whether the data is coming from a file, the network or some other arcane source is irrelevant for the user of the stream.

Yep, that's the consequence from the "generic reading" mentioned above :) Although many libraries nevertheless offer various methods of input management; e.g. the FreeType library has.

 

Well, just for clarification for the OP: There are also caveats to be considered: The library must not be allowed to read behind the logical end of data. If a stand-alone file is opened and wrapped by a stream then there is a natural end. Giving the library a stream on a network socket, a generous memory block or a package file may allow the library to read more bytes than intended for its purpose. This should be considered, e.g. by using an appropriate stream (if exists) or implementing a wrapping stream that reports an EOF if the logical end of data is reached. Similarly, if seeking is supported, the outermost stream may have to handle an appropriate offset.

Share this post


Link to post
Share on other sites

 


That's the nice thing about abstraction. A stream is just a source of data until it says it has no more data. Whether the data is coming from a file, the network or some other arcane source is irrelevant for the user of the stream.

Yep, that's the consequence from the "generic reading" mentioned above smile.png Although many libraries nevertheless offer various methods of input management; e.g. the FreeType library has.

 

Well, just for clarification for the OP: There are also caveats to be considered: The library must not be allowed to read behind the logical end of data. If a stand-alone file is opened and wrapped by a stream then there is a natural end. Giving the library a stream on a network socket, a generous memory block or a package file may allow the library to read more bytes than intended for its purpose. This should be considered, e.g. by using an appropriate stream (if exists) or implementing a wrapping stream that reports an EOF if the logical end of data is reached. Similarly, if seeking is supported, the outermost stream may have to handle an appropriate offset.

 

Ah, got it. If I recall correctly, I saw a reader function in the documentation for jsoncpp that had parameters for the beginning and end of the file. At the moment, my game has no networking capabilities (though, it is two player, so it's in the eventual "todo" :P), but I'll keep an eye out for the memory block size. Is this mostly an issue for performance? I could see how it could cause significant slowdown if used within the game loop with too large a memory block to read. I think for the moment, I'll pretty much be reading everything at start-up, and setting the variables within the program, and then writing everything back down to file on save.

Share this post


Link to post
Share on other sites

Alright, so, I think I've a loose handle on this.

After reading about IO all evening, I've come up with this:

I had a few troubles figuring out how to access the particular...node(?) or what-have-you of the json file, but stumbled upon it eventually. But, it seems to work. I think I can figure out how to incorporate it into my program without too many issues.

std::string lineOne;
std::string lineTwo;
try 
{ 
    std::ifstream TESTFILE; 
    TESTFILE.open("test2.json", std::ios::in);

    std::getline(TESTFILE, LineOne);//unsure why these two lines are necessary. Leftover from previous IO testing
    std::getline(TESTFILE, LineTwo);//without them, the reader will return NULL values though...
    Json::Value root; 
    Json::Reader reader; 

    bool parsedSuccess = reader.parse(TESTFILE, root, false); 

    if(not parsedSuccess) 
    { 
        std::cout<<"failed, damnit"<<std::endl; 
    } 
    else 
    { 
        std::cout<<"yay"<<std::endl; 
    } 

    const Json::Value testValue(root["firstValue"]);//name of first value in my example file 
    std::cout<<"The first value is "<<testValue<<std::endl; TESTFILE.close(); 
} 
catch(std::exception X) { std::cout<<"whoops, exception"<<std::endl; } 

Though, I seriously have the damnedest time reading documentation. I guess I can understand what any particular function is kind of doing, but I seriously have a difficult time figuring out how it all fits together. Maybe that just comes with time, but right now, for me, it's like trying to understand how to speak a language just by reading a dictionary. Trial and error pretty much ended up winning out today, over actually understanding the documentation tongue.png

 

Anyhow, thanks again to both of you, Haegar and BitMaster, you've helped a ton! smile.png

 

Edit**

I had taken it out of my original example, as they were just part of my testing of various IO functions,  but realized that without using std::getline() on the openfile, the JSON reader will return null values. I can't quite figure out why I'm needing to do that first. Shouldn't the reader function be able to parse that without getting the lines first?

Edited by Misantes

Share this post


Link to post
Share on other sites


Though, I seriously have the damnedest time reading documentation. I guess I can understand what any particular function is kind of doing, but I seriously have a difficult time figuring out how it all fits together. Maybe that just comes with time, but right now, for me, it's like trying to understand how to speak a language just by reading a dictionary. Trial and error pretty much ended up winning out today, over actually understanding the documentation

This is a reason why tutorials exist. A (good) tutorial explains how to use what in which situations, while a documentation enumerates all possibilities without regarding use cases much. Tutorials are much more suitable for learning, and documentation is good for looking up details or things learned earlier but forgotten for now.

Share this post


Link to post
Share on other sites


This is a reason why tutorials exist. A (good) tutorial explains how to use what in which situations, while a documentation enumerates all possibilities without regarding use cases much. Tutorials are much more suitable for learning, and documentation is good for looking up details or things learned earlier but forgotten for now.

Yeah, I've been unable to find any jsoncpp tutorials though. The couple I've come across have been for rather specific things within jsoncpp and weren't terribly useful. I've come across plenty for writing JSON and c++ IO functions in general, and they've been quite useful the last couple days.

 

Though, I just realized I'd only been searching Duckduckgo.com, which wasn't returning anything really useful, but I just tried google and it turned up a few tutorials I'll take a look at later. Kind of a common problem with duckduckgo...cest la vie.

Share this post


Link to post
Share on other sites

Maybe this function will benefit you:

#include <fstream>
#include <stringstream>

//Loads the entire file, newlines and all, and returns its contents as a string.
//Returns an empty string if the file doesn't exist.
std::string LoadFileAsString(const std::string &filename, bool *successful = nullptr)
{
	if(successful) *successful = false;

	//Open the file for reading.
	std::ifstream file(filename.c_str());
	if(!file)
	{
		//Insert your own error handling here.
                /*
		Log::Message(MSG_SOURCE("FileFunctions", Log::Severity::Error))
                            << "Failed to load '" << Log_HighlightCyan(GetFilenameFromPath(filename)) << "' at " << Log_DisplayPath(filename)
			    << "\nDo I have sufficient privileges? Does the file even exist?" << Log::FlushStream;
                */

		return "";
	}

	//Read the file into the stringStream.
	std::ostringstream stringStream;
	stringStream << file.rdbuf();

	if(successful) *successful = true;

        //Convert the stringStream into a regular string.
	return stringStream.str();
}

It's just a helper function I use alot in my own code. You can then take the string and pass it to whatever parsing function you want.

 

You can then go:

bool successful = false;
std::string json_example = LoadFileAsString("../file.txt", &successful);

if(!successful)
{
   //Insert proper error handling here.
   return;
}

Json::Value root;
Json::Reader reader;
bool parsedSuccess = reader.parse(json_example, root, false);

if(!parsedSuccess)
{
    //Insert proper error handling here.
    return;
}

Don't use "not", use '!'. While 'not' and 'and','or', and things like that are all standard C++, the vast majority of C++ programmers use ! && || and so on, so that's the way you need to learn it.

 

 

The reason why jsoncpp doesn't have file loading functions (or, seems to not - I haven't used that library before), is because it doesn't make sense for every library everywhere to re-invent the wheel with file-loading functions, when instead you can just use standard file-loading functions, and pass the loaded results to the 3rd-party libraries as needed. Part of programming is getting different functions from different libraries, and using them together like lego bricks to build more complex programs.

Edited by Servant of the Lord

Share this post


Link to post
Share on other sites

Don't use "not", use '!'. While 'not' and 'and','or', and things like that are all standard C++, the vast majority of C++ programmers use ! && || and so on, so that's the way you need to learn it.

I'll admit to some copy/pasting here tongue.png Generally I use !, &&, ||. However, In my test files, it's just quicker to copy/paste things from a tutorial and then play with them until I understand them, see how things are working. Though, copy/pasting isn't something I do in my programs typically, unless it's from another program of mine.

 

Servant, while I have your ear, and you seem to be familiar with jsoncpp, the example you gave uses the string parse function. Is there a way to use the istream parsing function without first getting strings of all the lines beforehand? In my example, I'm trying to use the istream parse function, however, unless I manually getline() on each line, it returns NULL (in which case, I may as well just use the string parse, per your example). It's not a giant deal, but it could cut down on some unnecessary code for me.

 

It's possible that that function doesn't actually do what i'm wanting it to do, which is parse the file without converting it to a string first. And it's a little of a moot point, since it's not a lot of extra code to convert to a string and I'll be running this at startup/shutdown. 

 

edit* just re-read your comment, and you mention you're actually not familiar with it, so no worries. However, if anyone IS familiar with jsoncpp and knows the answer to this, I'm all ears smile.png

 

edit** Ugh, actually the istream parse function works exactly like expected, I just had my json file formatted incorrectly. Good times :P Anyhow, I think for the moment, I have everything more or less sorted out to move forward. Thanks for the help and advice everyone. I appreciate it :)

Edited by Misantes

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement