Reading Json file's root in c++ with jsoncpp - c++

File:
{
"somestring":{
"a":1,
"b":7,
"c":17,
"d":137,
"e":"Republic"
},
}
how can I read the somestring value by jsoncpp?

Use the getMemberNames() method.
Json::Value root;
root << jsonString;
Json::Value::Members propNames = root.getMemberNames();
std::string firstProp = propNames[0];
std::cout << firstProp << '\n'; // should print somestring
If you want to see all the properties, you can loop through it using an iterator:
for (auto it: propNames) {
cout << "Property: " << *it << " Value: " << root[*it].asString() << "\n";
}
This simple loop will only work for properties whose values are strings. If you want to handle nested objects, like in your example, you'll need to make it recursive, which I'm leaving as an exercise for the reader.

Related

How do I check whether an index of array is empty and then, if it is, skip to the next?

I'm trying to build a program that can register a user to the database (still learning cpp, I hope that in the near future I'll be able to work with database).
What I'm trying to do with this code is to check whether an index of array is empty for the user to store an ID in it. If it isn't empty, I want the program to keep looking for an empty index of array, for the new info to be stored in.
Here is the code:
void registro() {
std::string userid[3];
userid[0] = "Houkros"; // eventually I'll try to have this being read from a file or server database..
std::string userpass[3];
std::string usermail[3];
std::string userkey[3];
std::string getUid[3];
std::string getUpass[3];
std::string getUmail[3];
std::string getUkey[3];
std::cout << std::endl << " >>>> REGISTRATION <<<< " << std::endl;
std::cout << " =============================================== " << std::endl;
std::cout << std::endl;
std::cout << "Please, enter the desired user id: " << std::flush;
if (userid[0].empty())
{
std::cin >> userid[0];
}
else {
std::cin >> userid[1];
}
for (int i = 0; i < 2; i++)
{
std::cout << " Element of array: " << i << " is > " << userid[i] << std::endl;
}
Please consider the following definitions for an "empty" array element:
a) not initialised (unhelpful, cannot be checked)
b) never yet written to (same as a) )
c) contains "" (possible, but means that "" must not be accepted as an actual content)
d) is empty according to a second array in which that info is maintained (this is what I almost recommend)
e) contains a struct with a string and a maintained "empty" flag (this I recommend)
Whatever you do, make sure that you init all variables and array elements before first read-accessing them; i.e. in all cases first write something meaningful to it.

How to save specific values in a list to txt using jsoncpp?

I have yahoo finance json file from which I want to isolate Date,Close and volume from the quote list and save it in the same order with a comma separtion in a single text file. This is my json script.
Json::Value root; // will contains the root value after parsing.
Json::Reader reader;
bool parsingSuccessful = reader.parse( YahooJson, root );
if(not parsingSuccessful)
{
// Report failures and their locations
// in the document.
std::cout<<"Failed to parse JSON"<<std::endl
<<reader.getFormatedErrorMessages()
<<std::endl;
return 1;
}else{
std::cout<<"\nSucess parsing json\n"<<std::endl;
std::cout << root<< std::endl;
std::cout <<"No of Days = "<< root["query"]["count"].asInt() << std::endl;
//below for loop returns an error
for (auto itr : root["query"]["result"]["quote"]) {
std::string val = itr.asString();
}
}
I was able to succed in fetching the json values and print root["query"]["count"].asInt() but when I go to the list values(quote) I dont know how to iterate through quote (query->result->quote) to get Date,close and volume values?
EDIT
Also tried this method
const Json::Value& quotes = root["query"]["results"]["quote"];
for (int i = 0; i < quotes.size(); i++){
std::cout << " Date: " << quotes[i]["Date"].asString();
std::cout << " Close: " << quotes[i]["Close"].asFloat();
std::cout << " Volume: " << quotes[i]["Volume"].asFloat();
std::cout << std::endl;
}
It works only when output was Date. For close and volume output it exits with a runtime error message and also this error
what() type is not convertible to string
You haven't specified which JSON library you are using, and I don't know the Yahoo finance data well enough to know the exact field names, but if you are using the JsonCpp library, which has documentation here, and you are asking about how to iterate over a JSON array, then one way to do it using iterators would look something like this
const Json::Value quote = root["query"]["results"]["quote"];
for (Json::ValueConstIterator itr = quote.begin(); itr != quote.end(); ++itr)
{
const Json::Value date = (*itr)["Date"];
const Json::Value close = (*itr)["Close"];
const Json::Value volume = (*itr)["Volume"];
std::cout << "Date: " << date.asString() << std::endl;
std::cout << "Close: " << close.asString() << std::endl;
std::cout << "Volume: " << volume.asString() << std::endl;
}

Pugixml C++ parsing XML

I am a newbie in pugixml. Consider I have XML given here. I want to get value of Name and Roll of Every Student. The code below only find the tag but not the value.
#include <iostream>
#include "pugixml.hpp"
int main()
{
std::string xml_mesg = "<data> \
<student>\
<Name>student 1</Name>\
<Roll>111</Roll>\
</student>\
<student>\
<Name>student 2</Name>\
<Roll>222</Roll>\
</student>\
<student>\
<Name>student 3</Name>\
<Roll>333</Roll>\
</student>\
</data>";
pugi::xml_document doc;
doc.load_string(xml_mesg.c_str());
pugi::xml_node data = doc.child("data");
for(pugi::xml_node_iterator it=data.begin(); it!=data.end(); ++it)
{
for(pugi::xml_node_iterator itt=it->begin(); itt!=it->end(); ++itt)
std::cout << itt->name() << " " << std::endl;
}
return 0;
}
I want the output of Name and Roll for each student. How can I modify above code? Also, if one can refer here(press Test), I can directly write xpath which is supported by pugixml. If so, how can I get the values I seek using Xpath in Pugixml.
Here's how you can do it with just Xpath:
pugi::xpath_query student_query("/data/student");
pugi::xpath_query name_query("Name/text()");
pugi::xpath_query roll_query("Roll/text()");
pugi::xpath_node_set xpath_students = doc.select_nodes(student_query);
for (pugi::xpath_node xpath_student : xpath_students)
{
// Since Xpath results can be nodes or attributes, you must explicitly get
// the node out with .node()
pugi::xml_node student = xpath_student.node();
pugi::xml_node name = student.select_node(name_query).node();
pugi::xml_node roll = student.select_node(roll_query).node();
std::cout << "Student name: " << name.value() << std::endl;
std::cout << " roll: " << roll.value() << std::endl;
}
I think that the reason why you are getting the "tags/nodes" instead of their values is because you are using the name() function instead of value(). Try replacing your itt->name() with itt->value() instead.
I found some good documentation about accessing document data here
Thanks #Cornstalks for the insight of using xpath in pugixml. I used child_value given here. The code of mine was thus:
for(pugi::xml_node_iterator it=data.begin(); it!=data.end(); ++it)
{
for(pugi::xml_node_iterator itt=it->begin(); itt!=it->end(); ++itt)
std::cout << itt->name() << " " << itt->child_value() << " " << std::endl;
}
I could also use xpath as #Cornstalks suggested thus making my code as:
pugi::xml_document doc;
doc.load_string(xml_mesg.c_str());
pugi::xpath_query student_query("/data/student");
pugi::xpath_query name_query("Name/text()");
pugi::xpath_query roll_query("Roll/text()");
pugi::xpath_node_set xpath_students = doc.select_nodes(student_query);
for (pugi::xpath_node xpath_student : xpath_students)
{
// Since Xpath results can be nodes or attributes, you must explicitly get
// the node out with .node()
pugi::xml_node student = xpath_student.node();
pugi::xml_node name = student.select_node(name_query).node();
pugi::xml_node roll = student.select_node(roll_query).node();
std::cout << "Student name: " << name.value() << std::endl;
std::cout << " roll: " << roll.value() << std::endl;
}
In your inner loop change the following line to get the values like :
student1 and 111 and so on...
std::cout << itt.text().get() << " " << std::endl;

How to print a map? And make it a global variable? C++

For Windows 7 64-bit
So I set up my map
map<string, string> database;
database["user"] = "123";
It's part of the main function, but how can I print the contents of the map? And most importantly how can it be turned into a global variable so I can use it by other functions? I'm trying to put my printing map process in a different function than where the map was made.
You can iterate through map with following code:
for(auto it = database.begin(); it != database.end(); ++it) {
// it->first is your key
// it->second is value of particular key
std::cout << "Key: " << it->first << std::endl;
std::cout << "Value: " << it->second << std::endl;
// value can be reached as follows as well
std::cout << "Value: " << database[it->first] << std::endl;
}
Try this:
#include <map>
#include <iostream>
// this will be a global variable
map<string, string> database;
int main()
{
database["user"] = "123";
map<string, string>::iterator it;
for(it = database.begin(); it!= database.end(); it++)
{
pair<string, string> p = *it;
cout << "key=\"" << p.first << "\" value=\"" << p.second << "\"" << endl;
}
}
I'd advise to use C++11 features, like for_each, range based begin and end and lambda:
std::for_each(std::begin(database), std::end(database),
[&database](std::pair<std::string, std::string> p) {
std::cout << "Key : " << p.first << "\tValue : " << p.second << std::endl;
});
Second I'd avoid to make your map a global variable. There are reasons for global definitions, but for a container? (This maybe depends how big your project is, but I'd say let's get rid of any global variable, and if it is really needed put it inside a namespace)
Why not encapsulate your map into a class and provide functions to alter the content of your map, even your printing facility? If you want to make sure, that every user of your class shall have the same content of the map (translated this means the same instance), you can turn your class into a singleton.
If you have C++1 available (and as a sidenote to Stefan's answer: You don't actually need std::for_each or lambdas in C++11), iteration can be done using range based for:
for ( std::pair<std::string,std::string> const &p : database)
std::cout << "key=\"" << p.first << "\" value=\"" << p.second << "\"" << std::endl;
If you want to use it in other functions, think about several things instead of making the variable global:
Aggreate/incapsulate all functions using the database in a class
Pass the map object to your functions

C++ STL map with custom comparator storing null pointers

I'm trying to write a copy constructor for an object managing a STL map containing pointers, where the key is a string. However, when I attempt to insert new values in the map, the pointers are set to NULL:
// ...
for(std::map<std::string, data_base*, order>::const_iterator it = other.elements.begin();
it != other.elements.end(); ++it){
data_base *t = it->second->clone();
std::cout << "CLONE: " << std::hex << t << std::endl;
elements[it->first] = t;
std::cout << "INSERTED: " << std::hex << elements[it->first] << std::endl;
}
// ...
other is the object being copied and elements the map. The clone() method returns a pointer to a new object (via new).
Running the code above I get something like:
CLONE: 0xcfbbc0
INSERTED: 0
I'm not a very experienced programmer and this issue is probably simple to fix, but I didnt find any solution to it searching around.
Thanks a lot for your time.
I don't see any problem with this code, other than maybe
std::map<std::string, data_base*, order>::const_iterator it
Here order gives the key comparator to use to sort the pairs contained in the map (often implemented as a tree).
Maybe you're doing something wrong in it, making your [] operator don't find the right ke, making your last line logging a new pair with a null ptr.
First, try without that order, using the default key-comparator (std::less), then if it don't work, post your order definition and the map declaration. If it's not enough, just provide a simple complete program that reproduce the problem.
I just wrote a simple similar test, using the default key-comparator :
#include <map>
#include <string>
#include <iostream>
struct Data
{
int k;
Data* clone() { return new Data(); }
};
typedef std::map< std::string, Data* > DataMap;
DataMap data_map;
int main()
{
data_map[ "hello" ] = new Data();
data_map[ "world" ] = new Data();
DataMap other_map;
for( DataMap::const_iterator it = data_map.begin(); it != data_map.end(); ++it)
{
Data*t = it->second->clone();
std::cout << "CLONE: " << std::hex << t << std::endl;
other_map[it->first] = t;
std::cout << "INSERTED: " << std::hex << other_map[it->first] << std::endl;
}
std::cin.ignore();
return 0;
}
On VS2010SP1, this outputs :
CLONE: 00034DD0
INSERTED: 00034DD0
CLONE: 00035098
INSERTED: 00035098
So it should be the problem, or maybe you're doing something wrong before.
Try this out, to help debug the issue. I'd recommend double-checking that the order function is correct. You can remove it to use std::less<T>, which is known to work.
// ...
typedef std::map<std::string, data_base*, order> string_db_map;
for(string_db_map::const_iterator it = other.elements.begin();
it != other.elements.end();
++it)
{
data_base *t = it->second->clone();
std::cout << "CLONE: " << std::hex << t << std::endl;
std::pair<string_db_map::iterator, bool) result = elements.insert(
string_db_map::value_type( it->first, t));
if ( !result.second )
{
std::cout << "element['" << it->first << "'] was already present, and replaced." << std::endl;
}
std::coud << "INSERTED [iterator]: " << std::hex << (*result.first).second << std::endl;
std::cout << "INSERTED [indexed]: " << std::hex << elements[it->first] << std::endl;
}
// ...