Find all keys JSON - RapidJSON - c++

I need to find all the keys in the kTypeNames[] with rapidJSON library.
Trying to iterate all the nodes but I'm missing something; here's the code:
#include <iostream>
#include <fstream>
#include <string>
#include <bits/stdc++.h>
#include <unistd.h>
#include "rapidjson/document.h"
#include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.h"
using namespace rapidjson;
using namespace std;
const char* kTypeNames[] = { "id", "text", "templ_text", "key" };
int main(int argc, char* argv[]) {
string line;
char json[65000];
std::ifstream file(argv[1]);
unsigned long i = 0;
if (file.is_open()) {
while (!file.eof()) {
file.get(json[i]);
i++;
}
file.close();
} else {
cout << "Unable to open file";
}
Document document;
document.Parse(json);
printf("\n\n\n\n*********Access values in document**********\n");
assert(document.IsObject());
for (auto Typename : kTypeNames) {
if (document.HasMember(Typename)) {
cout << "\n";
cout << Typename << ":" << document[Typename].GetString()<< endl;
cout << "\n";
}
else {
cout << "\n None\n";
}
}
It does not works with a nested JSON.
{
"node": {
"text": "find this",
"templ_text": "don't find",
"ver": "don't find"
},
"ic": "",
"text": "also this",
"templ_text": "don't care",
"par": {
"SET": {
"vis": "<blabla>",
"text": "keyFound",
"templ_text": "don't need this"
}
}
}
This is the output:
None
text:also this
templ_text:don't care
None
I would like to find all the "text" keys
How can I iterate through all the nodes/ json document?

The code you have is just searching for a list of pre-defined keys directly within the document root (document.HasMember is not a recursive search!).
You could just loop through the document nodes recursively. For example for object/map nodes, you loop on the MemberBegin() and MemberEnd() iterators, similar to a std::map or other standard containers.
for (auto i = node.MemberBegin(); i != node.MemberEnd(); ++i)
{
std::cout << "key: " << i->name.GetString() << std::endl;
WalkNodes(i->value);
}
Array uses Begin() and End(). Then, when you encounter a node with a "text" member, you can output the value of that node (i->value).
Alternatively, rather than using a Document DOM object, you can do it with the parser stream. Rapidjson uses a "push" API for this, where it calls methods you define in a class as it encounters each piece of JSON. Specifically, it will call a Key method.
class MyHandler : public BaseReaderHandler<UTF8<>, MyReader> {
bool Key(const char* str, SizeType length, bool copy)
{
std::cout << "Key: " << str << std::endl;
}
...
};
MyHandler handler;
rapidjson::Reader reader;
rapidjson::StringStream ss(json);
reader.Parse(ss, handler);
This gets a bit more complex, you will want to set a flag of some sorts, and then output the next value callback after.
class MyHandler : public BaseReaderHandler<UTF8<>, MyReader> {
bool Key(const char* str, SizeType length, bool copy)
{
isTextKey = strcmp(str, "text") == 0; // Also need to set to false in some other places
return true;
}
bool String(const char* str, SizeType length, bool copy)
{
if (isTextKey) std::cout << "text string " << str << std::endl;
return true;
}
...
bool isTextKey = false;
};
Also remember, that JSON allows a null within a string \0, which is why also have the size parameters and members, as well as Unicode. So to fully support any JSON document that needs accounting for.

Related

creating a class vector that does not delete it's content

I am a beginner , so i wanted to ask , can we create a class object vector/array , that does not delete it's content when i close the program like , so like I want a customer record , but whenever if we try to restart the program we need to enter the customer details again and again ...
how to prevent that from happening
#include <iostream>
#include <vector>
using namespace std;
class customer{
public:
int balance;
string name;
int password;
};
int main(){
vector <customer> cus;
...
if(choice == 1){
cout << cus[i].balance
}
return 0;
}
As a complement to Adam's answer, it is possible to encapsulate the serialization in the container class itself. Here is an simplified example:
The header file defining a persistent_vector class that saves its content to a file:
#include <iostream>
#include <string>
#include <vector>
#include <fstream>
#include <initializer_list>
namespace {
// Utility functions able to store one element of a trivially copyable type
template <class T>
std::ostream& store1(std::ostream& out, const T& val) {
out.write(reinterpret_cast<const char*>(&val), sizeof(val));
return out;
}
template <class T>
std::istream& load1(std::istream& in, T& val) {
in.read(reinterpret_cast<char*>(&val), sizeof(val));
return in;
}
// Specialization for the std::string type
template <>
std::ostream& store1<std::string>(std::ostream& out, const std::string& val) {
store1<size_t>(out, val.size());
if (out) out.write(val.data(), val.size());
return out;
}
template <>
std::istream& load1<std::string>(std::istream& in, std::string& val) {
size_t len;
load1<size_t>(in, len);
if (in) {
char* data = new char[len];
in.read(data, len);
if (in) val.assign(data, len);
delete[] data;
}
return in;
}
}
template <class T>
class persistent_vector {
const std::string path;
std::vector<T> vec;
// load the vector from a file
void load() {
std::ifstream in(path);
if (in) {
for (;;) {
T elt;
load1(in, elt);
if (!in) break;
vec.push_back(elt);
}
if (!in.eof()) {
throw std::istream::failure("Read error");
}
in.close();
}
}
// store the vector to a file
void store() {
std::ofstream out(path);
size_t n = 0;
if (out) {
for (const T& elt : vec) {
store1(out, elt);
if (!out) break;
++n;
}
}
if (!out) {
std::cerr << "Write error after " << n << " elements on " << vec.size() << '\n';
}
}
public:
// a bunch of constructors, first ones load data from the file
persistent_vector(const std::string& path) : path(path) {
load();
}
persistent_vector(const std::string& path, size_t sz) :
path(path), vec(sz) {
load();
};
// last 2 constructors ignore the file because they do receive data
persistent_vector(const std::string& path, size_t sz, const T& val) :
path(path), vec(sz, val) {
};
persistent_vector(const std::string& path, std::initializer_list<T> ini) :
path(path), vec(ini) {
}
// destructor strores the data to the file before actually destroying it
~persistent_vector() {
store();
}
// direct access to the vector (const and non const versions)
std::vector<T>& data() {
return vec;
}
const std::vector<T>& data() const {
return vec;
}
};
It can, out of the box, handle any trivially copyable type and std::string. User has to provide specializations of store1 and load1 for custom types.
Here is a trivial program using it:
#include <iostream>
#include <string>
#include "persistent_vector.h"
int main() {
std::cout << "Create new vector (0) or read an existing one (1): ";
int cr;
std::cin >> cr;
if (!std::cin || (cr != 0 && cr != 1)) {
std::cout << "Incorrect input\n";
return 1;
}
if (cr == 0) {
persistent_vector<std::string> v("foo.data", 0, "");
// skip to the end of line...
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
for (;;) {
std::string line;
std::cout << "Enter a string to add to the vector (empty string to end program)\n";
std::getline(std::cin, line);
if (line.empty()) break;
v.data().push_back(line);
}
}
else {
persistent_vector<std::string> v("foo.data");
for (const std::string& i : v.data()) {
std::cout << i << '\n';
}
}
return 0;
}
When a programmer creates a vector class, he must ensure that the resources acquired for that vector are released when they are no longer needed. (See RAII)
C++ Reference : https://en.cppreference.com/w/cpp/language/raii
Wikipedia : https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization
Stack Overflow : What is meant by Resource Acquisition is Initialization (RAII)?
Microsoft : https://learn.microsoft.com/en-us/cpp/cpp/object-lifetime-and-resource-management-modern-cpp?view=msvc-170
Before the program closes, all resources must be released.
(No leaking resources, memory included)
It is not possible to create a vector class that does not delete its contents after closing a program. Secure operating systems will release program resources when the program is closed.
If you want the program not to lose customer information after closing, you need to save the information in persistent (non-volatile) storage device, such as a disk.
As CinCout, 김선달, Serge Ballesta say, you have to save the customer information to a file, and write the program so that you can read that file during the start of the program.
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
struct customer {
std::string name;
int balance;
int password;
};
int main() {
std::vector <customer> customers;
std::ifstream ifs("info.txt");
{
customer customer{};
while (ifs >> customer.name >> customer.balance >> customer.password)
customers.push_back(customer);
}
for (const auto& [name, balance, password] : customers) {
std::cout <<
"\nName : " << name <<
"\nBalance : " << balance <<
"\nPassword : " << password <<
'\n';
}
std::cout << "\n\nWelcome\n\n";
std::ofstream ofs("info.txt", std::ios_base::app);
char cont{};
do {
customer customer{};
std::cout << "Name : ";
std::cin >> customer.name;
std::cout << "Balance : ";
std::cin >> customer.balance;
std::cout << "Password : ";
std::cin >> customer.password;
ofs << customer.name << ' ' << customer.balance << ' ' << customer.password << '\n';
std::cout << "Add another customer? (Y/N) : ";
std::cin >> cont;
} while (cont == 'Y');
for (const auto& [name, balance, password] : customers) {
std::cout <<
"\nName : " << name <<
"\nBalance : " << balance <<
"\nPassword : " << password <<
'\n';
}
}
CPlusPlus : https://www.cplusplus.com/doc/tutorial/files/
LearnCpp : https://www.learncpp.com/cpp-tutorial/basic-file-io/
(About File I/O)
This program is a prototype, I left some things incomplete (like check readings, user-defined I/O operators, duplicate code, formatting, reallocations of customers, ifs is not required after range-for + structured binding,...).
I suggest you read the book "Programming: Principles and Practice Using C+", I’m reading it and it helped me a lot.
(I’m also a beginner)
Edit: I also suggest you use "using namespace std;" only for small projects, examples or simple exercises.
Do not use "using namespace std;" for real projects, large projects or projects that may include other dependencies because the use of "using namespace std;" could lead to a possible naming collisions between names within std and the names of other codes and libraries.
It’s not good practice to use it all the time.

How can I parse a vector to a bool in C++?

I'm trying to write a program that first checks if a name is in a vector and if not then adds it to the vector. My code seems to have difficulties with parsing, at least that's what I get out of it. I tried changing the string to a char but it did not help me much.
#include <iostream>
#include <vector>
#include <string>
bool isinVector(std::string uElement, std::vector<std::string> uArray)
{
for (unsigned int i = 0; i <= sizeof(uArray); i++) {
if (uArray[i] == uElement) {
return true;
}
else {
return false;
}
}
}
int main()
{
bool trigger = false;
while (!trigger) {
std::vector<std::string> names;
names.push_back("Bart");
std::string newName;
getline(std::cin, newName);
if (isinVector(newName, names))
{
std::cout << "true" << std::endl;
trigger = true;
}
else
{
std::cout << "false" << std::endl;
names.push_back(newName);
for (int i = 0; i <= sizeof(names); i++) {
std::cout << names[i] << std::endl;
}
}
}
}
I made some adjustments to your code, removing your isinVector function and using a lambda inside the main function instead. In the future please provide a concise question and example.
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using std::vector;
using std::string;
using std::cout;
using std::cin;
using std::endl;
using std::find_if;
int main(){
bool trigger = false;
while (!trigger) {
vector<string> names;
names.push_back("Bart");
string newName;
getline(cin, newName);
if(find_if(names.begin(), names.end(), [newName] (const string& name){
return !name.compare(newName);
}) != names.end()){
cout << "true" << endl;
trigger = true;
}
else{
cout << "false" << endl;
names.push_back(newName);
for (size_t i = 0; i < names.size(); i++) {
cout << names.at(i) << endl;
}
}
}
return 0;
}
The code uses std::find_if to check if the element exists in the vector. If std::find_f does not return the iterator to uArray.end() Then the element exists. Also your for loop used sizeof which is incorrect, use the vector.size method. And you were looping until <= , it should be < uArray.size() And it's safer to access elements in the vector through the .at method rather than an index [] since the .at will throw an out_of_range exception.
Among the things wrong in the updated post.
Improper use of sizeof
Reinventing a standard algorithm
Lack of error checking
Consider the tasks you're trying to accomplish. You want to:
Initialize a starting vector containing the name Bart
Continuously read new names. For each new name read:
a. Check to see if it is already in the vector.
if it is present terminate the read loop
else add it to the vector, and print the entire vector
This sequence of operations can be accomplished with stepwise refinement.
Step 1. Read names
First, you need to be able to continuously read names:
#include <iostream>
#include <string>
int main()
{
std::string name;
while (std::getline(std::cin, name))
std::cout << name << '\n';
}
Simple enough. Running this will echo any strings you type, one at a time, separated by newlines.
Step 2. Accumulate names in a vector
Next, we need to add a vector to hold the strings we're reading, with an initial population of the name "Bart". For this pass we'll be just putting every string we read into the vector
#include <iostream>
#include <vector>
#include <string>
int main()
{
std::vector<std::string> names = { "Bart" };
std::string name;
while (std::getline(std::cin, name))
{
names.emplace_back(name);
for (auto const& s : names)
std::cout << s << ' ';
std::cout.put('\n');
}
}
In addition to what was done prior, we're now accumulating strings in the vector, including duplicates, and reporting the vector content after each name read. This gets us closer to our stated goal.
Step 3: Conditional loop exit based on duplicate detection
Now we need to check for duplicates, and terminate the loop once it happens. We can do this using std::find. The final code is below:
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
int main()
{
std::vector<std::string> names = { "Bart" };
std::string name;
while (std::getline(std::cin, name))
{
if (std::find(names.begin(), names.end(), name) != names.end())
break;
names.emplace_back(name);
for (auto const& s : names)
std::cout << s << ' ';
std::cout.put('\n');
}
}
That's it. This is a simple task, but it lends itself nicely to an example of how you break a multi-part task down to manageable objectives , then build it in pieces.
Hope you found it useful.
Now my code looks like this:
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
bool isinVector (std::string uElement, std::vector<std::string> uArray) {
bool invector = false;
std::vector<std::string>::iterator it = std::find(uArray.begin(),
uArray.end(),uElement);
if(it != uArray.end()){
invector = true;
}
return invector;
}
int main(){
bool trigger = false;
std::string name;
std::vector<std::string> names = { "Bart" };
while (std::getline(std::cin, name)){
if (isinVector(name, names)) {
std::cout << "true" << std::endl;
break;
}
else
{
std::cout << "false" << std::endl;
names.emplace_back(name);
}
}
return 0;
}
and it works, thanks a lot guys!

map inserting keys but not value

I am playing with c++ code today. Learning about std containers. I'm trying to insert and update data in a std::map but for some reason I can't insert values into a map. Keys will insert but not values. The code at the bottom will print the following if you enter something into the terminal that opens. In this example I entered "test". Anyway, my questions are, why is the insert returning false, why in the value not inserting?
test
first
failed
Context1 :
Here is the code:
#include "stdafx.h"
#include <string>
#include <iostream>
#include <map>
#include <random>
static std::map<std::string, std::string> currentFullState;
static const std::string sDEFAULT_STRING = "";
void PringCurrentState()
{
std::map<std::string, std::string>::iterator stateData = currentFullState.begin();
while (stateData != currentFullState.end())
{
std::cout << stateData->first << " : ";
std::cout << stateData->second << std::endl;
stateData++;
};
}
void UpdateState(std::string context, std::string data)
{
if (currentFullState[context] == sDEFAULT_STRING)
{
// first entry, possibly special?
std::cout << "first" << std::endl;
auto result = currentFullState.insert(std::make_pair(context, data.c_str()));
if (result.second == false)
std::cout << "failed" << std::endl;
else
std::cout << "good" << std::endl;
}
else if (data != currentFullState[context])
{
// change in value
}
else
{
currentFullState[context] == data;
}
}
void DoWork()
{
if (rand() % 2)
{
UpdateState("Context1", "Data1");
}
else
{
UpdateState("Context2", "Data2");
}
}
int main()
{
std::string command = "";
for (;;)
{
PringCurrentState();
std::cin >> command;
DoWork();
if (command == "q")
{
break;
}
}
return 0;
}
Why does the insert not work?
Certainly would help if you wrote
currentFullState[context] = data;
instead of
currentFullState[context] == data;
Also
auto result = currentFullState.insert(std::make_pair(context, data));
should be preferred to
auto result = currentFullState.insert(std::make_pair(context, data.c_str()));
Slightly surprised that the second one compiles.
=========================================================================
The real reason the insert fails is that you are adding that key for the second time. This is the first time
if (currentFullState[context] == sDEFAULT_STRING)
operator[] on a map always adds the key to the map. This is why your second attempt to add with
auto result = currentFullState.insert(std::make_pair(context, data.c_str()));
fails, the key is already present. If you had written
currentFullState[context] = data;
Then it would work.

Search for key by vector in map

So, we have a school-project in creating a phonebook where you should be able to look up phone numbers by searching for the name. I decided to use a map with a string for the phone number and and a vector of strings for the name, due associated number should be able to have multiple names in it.
However, due to us jumping straight from Python to C++ without any explanation of the syntax or the language, I am having a hard time coming up with a way to look for the number by searching for names.
The class I am using looks like this
class Telefonbok
{
public:
void add(string namn, string nummer)
{
map<string, vector<string>>::iterator it = boken.find(nummer);
if (it != boken.end())
{
cout << "This number already exists, please choose another";
}
else
{
namn_alias.push_back(namn);
boken[nummer] = namn_alias;
}
}
void lookup(string name)
{
for (map<string, vector<string>>::iterator sokning = boken.begin(); sokning != boken.end(); sokning++)
cout << "Hello!";
}
private:
vector<string> namn_alias;
string nummer;
map<string, vector<string>> boken;
};
What I am trying to do in lookup function is to search for a phone number by the names in the vector, but I am stumped on how to proceed with looking through the vector inside the for-loop.
The plan was to go through the Map keys one by one to find the vector that contains the searched-for name. Any tips on how to proceed or some functions I have missed that can be used for this?
Algirdas is correct, you should read up on C++.
Assuming you are mapping name to 1-or-more numbers, but only 1 number per name...
#include <cstddef>
#include <iostream>
#include <map>
#include <string>
#include <vector>
using std::cout;
using std::endl;
using std::map;
using std::string;
using std::vector;
class Telefonbok
{
public:
void add(string namn, string nummer) {
auto it = nummer_namn.find(nummer);
if (it != nummer_namn.end()) {
cout << "This number already exists, please choose another" << endl;
}
else {
nummer_namn[nummer] = namn;
namn_nummer[namn].push_back(nummer);
}
}
void lookup(string name) {
auto it = namn_nummer.find(name);
if (it == namn_nummer.end()) {
cout << "Unable to find any numbers for " << name << ", sorry." << endl;
return;
}
for (auto const& sokning : it->second)
cout << name << " : " << sokning << endl;
}
private:
map<string, vector<string>> namn_nummer;
map<string, string> nummer_namn;
};
int main() {
Telefonbok bok;
bok.add("Eljay", "789");
bok.add("Eljay", "456");
bok.add("Beaker", "123");
bok.lookup("Eljay");
bok.lookup("Beaker");
bok.lookup("Bunsen Honeydew");
return EXIT_SUCCESS;
}

Error in getting the array out of JSON string

I am trying to get the array from my JSON Stinrg defined in the main function. I have used libjson API for this, simple key value is easy to get so I am able to get the value of RootA but how about this array in ChildA. Please let me know
#include <iostream>
#include <libjson/libjson.h>
#include <stdio.h>
#include <string.h>
using namespace std;
char rootA[20];
int childB;
int *childInt;
void ParseJSON(JSONNODE *n) {
if (n == NULL) {
printf("Invalid JSON Node\n");
return;
}
JSONNODE_ITERATOR i = json_begin(n);
while (i != json_end(n)) {
if (*i == NULL) {
printf("Invalid JSON Node\n");
return;
}
// recursively call ourselves to dig deeper into the tree
if (json_type(*i) == JSON_ARRAY || json_type(*i) == JSON_NODE) {
ParseJSON(*i);
}
// get the node name and value as a string
json_char *node_name = json_name(*i);
// find out where to store the values
if (strcmp(node_name, "RootA") == 0) {
json_char *node_value = json_as_string(*i);
strcpy(rootA, node_value);
cout << rootA<<"\n";
json_free(node_value);
} else if (strcmp(node_name, "ChildA") == 0) {
JSONNODE *node_value = json_as_array(*i);
childInt=reinterpret_cast<int *>(&node_value);
cout << childInt[0]<<"\n";
cout << childInt[1]<<"\n";
json_free(node_value);
} else if (strcmp(node_name, "ChildB") == 0) {
childB = json_as_int(*i);
cout << childB;
}
// cleanup and increment the iterator
json_free(node_name);
++i;
}
}
int main(int argc, char **argv) {
char
*json =
"{\"RootA\":\"Value in parent node\",\"ChildNode\":{\"ChildA\":[1,2],\"ChildB\":42}}";
JSONNODE *n = json_parse(json);
ParseJSON(n);
json_delete(n);
return 0;
}
Thanks not-sehe but I got the solution for this
Ok I got it... treat array as a node and iterate over it again as if its a value with blank key. You can see the code part which did it..
if (json_type(*i) == JSON_ARRAY) {
cout << "\n Its a Json Array";
JSONNODE *arrayValue = json_as_array(*i);
JSONNODE_ITERATOR i1 = json_begin(arrayValue);
while (i1 != json_end(arrayValue)) {
cout << "\n In Array Loop ";
cout << json_as_int(*i1);
++i1;
}
}
This is probably not the answer you were looking for, but let me just demonstrate that a library with a slightly more modern interface makes this a lot easier (test.cpp):
#include <sstream>
#include "JSON.hpp"
int main()
{
auto document = JSON::readFrom(std::istringstream(
"{\"RootA\":\"Value in parent node\",\"ChildNode\":{\"ChildA\":[1,2],\"ChildB\":42}}"));
auto childA = as_object(
as_object(document)[L"ChildNode"]
)[L"ChildA"];
std::cout << childA << std::endl;
}
Which prints
[1,2]
It's using my own minimalist implementation of the rfc4627 specs. It's minimalist in interface only, supporting the full syntax and UNICODE.
The API interface is quite limited, but you can already see that working without C-style pointers, with proper dictionary lookups, key comparisons etc. makes it a less tedious and error prone:
// or use each value
for(auto& value : as_array(childA).values)
std::cout << value << std::endl;
// more advanced:
JSON::Value expected = JSON::Object {
{ L"RootA", L"Value in parent node" },
{ L"ChildNode", JSON::Object {
{ L"ChildA", JSON::Array { 1,2 } },
{ L"ChildB", 42 },
} },
};
std::cout << "Check equality: " << std::boolalpha << (document == expected) << std::endl;
std::cout << "Serialized: " << document;
See the full parser implementation (note: it includes serialization too) at github: https://github.com/sehe/spirit-v2-json/tree/q17064905