Access fields of a JSON array using RapidJSON in C++ - c++

I am new using the RapidJSON library and I want to know how I can access specific elements within an array in JSON format, this is the JSON:
{
"products": [
{
"id_product": 1,
"product_name": "Beer",
"product_price": 120.00
},
{
"id_product": 2,
"product_name": "Pizza",
"product_price": 20.00
}
]
}
I have this part of the code to make:
Document json_value;
json_value.Parse(json_message); //Above Json
if(!json_value.IsObject())
{
//ERROR
}
if(json_value.HasMember("products"))
{
const Value& a = json_value["products"];
if(a.IsArray())
{
for (SizeType i = 0; i < a.Size(); i++)
{
//Here i dont know how to access the elements of the array elements, but i need something like this:
int element_id = 1; //id_product = 1
string element_name = "Beer"; // product_name = "Beer"
float element_price = 120.00; //prodcut_price = 120.00
}
}
}

You can access the elements of the object using operator[]().
for (SizeType i = 0; i < a.Size(); i++)
{
if (a[i].IsObject()) {
int element_id = a[i]["id_product"].GetInt();
string element_name = a[i]["product_name"].GetString();
double element_price = a[i]["product_price"].GetDouble();
}
}
Please note that you might want to add checks for the type and existence of the member if your JSON is not always consistent.

Since for each "product" in products is an object, you can also iterate each key,value pair like this:
const Value& products = json_value["products"];
for (auto& product: products) { // products is type Array
for (auto& prod : product.GetObject()) {
std::cout << prod.name // similar to map->first
// i.e "id_product", "product_name"
<< prod.value // similar to map->second
// i.e 2, "Pizza"
<< std::endl;
}
}

I believe you can just call operator[]() with an unsigned int, if the value is an array. The tutorial appears to confirm this:
const Value& a = document["a"];
assert(a.IsArray());
for (SizeType i = 0; i < a.Size(); i++) // Uses SizeType instead of size_t
printf("a[%d] = %d\n", i, a[i].GetInt());
You can also get iterators using MemberBegin() and MemberEnd().

Related

How to return an array in method decleration using C++?

I am trying to write C++ code suitable for object oriented programming.
I have two classes, namely, Student and Course. In the Student class, I have quiz_scores which is a 1-D array of 4 integers. I need both set and get methods, both are used in natural common way.
In the following, I implement setQuizScores method:
void Student :: setQuizScores(int* quizscores){
for(int i = 0; i<4; i++){
quiz_scores[i] = quizscores[i];
}
Where quizscores are my private members.
Now, next thing is that I want to return this quiz_scores array in the getQuizScores for each students of Student class.
However, the problem is that C++ does not allow us to return arrays directly. Instead, I want the structure of my code as following:
int Student :: getQuizScores(){
Do something;
return the elements of quiz_scores;
}
How can I do that efficiently?
I prefer not to use the Standard Template Library (STL), so I need to create my own arrays and access them according to the explanation above.
There are a few ways how you could return an array:
Pass in an array to copy to
void Student::getQuizScores(int* out) {
for(int i = 0; i < 4; i++)
out[i] = quiz_scores[i];
}
Student student;
int scores[4];
student.getQuizScores(scores);
// use scores[0], etc...
return a struct containing the array
struct Scores {
int values[4];
};
Scores Student::getQuizScores() {
Scores s;
for(int i = 0; i < 4; i++)
s.values[i] = quiz_scores[i];
return s;
}
Student student;
Scores s = student.getQuizScores();
// use s.values[0], etc...
return a reference to the quiz_scores array inside the class
using Scores = int[4];
Scores const& Student::getQuizScores() const {
return quiz_scores;
}
Student student;
Scores const& scores = student.getQuizScores();
// use scores[0], etc...
Just as setQuizScores() is able to take a pointer to an array, so too can getQuizScores() return a pointer to the quiz_scores member array, eg:
const int* Student::getQuizScores() const {
// do something...
return quiz_scores;
}
The caller can then access the array elements as needed, eg:
Student s;
...
const int *scores = s.getQuizScores();
for(int i = 0; i < 4; ++i){
cout << scores[i] << ' ';
}
Alternatively, since the array is fixed size, you can return a reference to the array instead, eg:
typedef int scoresArr[4];
scoresArr quiz_scores;
...
const scoresArr& Student::getQuizScores() const {
// do something...
return quiz_scores;
}
Student s;
...
const scoresArr &scores = s.getQuizScores();
for(int i = 0; i < 4; ++i){
cout << scores[i] << ' ';
}
You can return a pointer to the quiz_scores array through getQuizScores method as shown below:
Version 1: Using trailing return type
auto getQuizScores() -> int(*)[4]
{
//Do something;
return &quiz_scores;//NOTE THE & INFRONT OF quiz_scores
}
Now you can use this returned pointer to initialize other arrays. One possible example would be:
#include <iostream>
struct Student
{
int quiz_scores[4]= {1,2,3,4};
//getQuizScores returns a pointer to an array of size 4 with element of type int
auto getQuizScores() -> int(*)[4]
{
//Do something;
return &quiz_scores;//NOTE THE & INFRONT OF quiz_scores
}
void setQuizScores(int* quizscores)
{
for(int i = 0; i<4; i++)
{
quiz_scores[i] = quizscores[i];
}
}
};
int main()
{
Student s;
int arr[4];
for(int i = 0; i< 4; ++i)
{
arr[i] = (*s.getQuizScores())[i];
std::cout<<arr[i]<<std::endl;
}
return 0;
}
Version 2: Without using trailing return type
int (*getQuizScores())[4]
{
//Do something;
return &quiz_scores;//NOTE THE & INFRONT OF quiz_scores
}
Version 2 is the same as version 1 except that this time the getQuizScores method does not uses trialing return type.
There are other possibilities also like returning a reference to the quiz_scores array.

Are multi-dimensional multi maps possible?

All the examples of multimap, yet to find a multi-dimensional example...
System: Visual Studio 2019, C++. Winapi with no extra libraries
Expected result: stores a multi-dimensional array of key/value pairs. Able to pull up a key pair by specifying parent key pairs (similar to hashes in Perl)
Using multimap instead of map since some of the fields have the same header.
Example of my data:
conversationID(int)
|
ConversationType(wstring)
lineID(int)
|
lineType(wstring)
originalLine(wstring)
taggedLine(wstring)
wordID
|
...
Declare the structure:
#include <map>
multimap< int, multimap <int, multimap <wstring, multimap <wstring, multimap <wstring, multimap<wstring, wstring> > > > > > conversation;
multimap< int, multimap <int, multimap <wstring, multimap <wstring, multimap <wstring, multimap<wstring, wstring> > > > > > ::iterator conversationIt;
Try to store some stuff in it... (error: "no instance of overloaded function")
conversation.insert(make_pair<int,int>(2, 1 make_pair<wstring, wstring>(L"originalLine", line)));
Also tried the following but found that multimap does not support [] insert:
conversation[0][0][L"originalLine"][line];
Based on the tree diagram above, the best approach is a simple multidimensional array.
this allows for duplicate "keys"
mixed variable types can be achieve by using supporting tables (see example below)
Compared to other methods:
map does not allow duplicate keys
multimap. could not get this to work. tried for weeks.
class inheritance. does not link sub branches to branches above. it is just a method for exposing variables and methods without having to duplicate your work.
also looked at multisets, vectors, etc.
note: for a large multidimensional array, you will need to use heap memory.
void conversationArray(LPWSTR line)
{
auto conversation = new int[5][10000][100][50]; // conversationID, lineID, objectID, tagid = tag/text
wstring conversationType[3];
auto lineType = new wstring[5][10000]; // conversationID, lineID = string
wstring text[50];
conversationType[0] = L"chat gui";
lineType[0][0] = L"chat";
conversation[0][0][14][12] = 25;
conversation[0][0][15][12] = 30;
lineType[0][1] = L"chat1";
conversation[0][1][15][12] = 500;
lineType[0][2] = L"chat2";
conversation[0][2][15][12] = 60;
conversationType[1] = L"chat gui1";
lineType[1][0] = L"chat-also";
conversation[1][0][15][12] = 33;
// if tag id = 0 then tag is text
conversation[0][0][14][0] = 0;
conversation[0][0][15][0] = 20;
text[0] = line;
text[20] = L"dog";
// print out
int records = 0;
for (int conversationID = 0; conversationID < 5; conversationID++)
{
//sendToReportWindow(L"conversationID: %d\n", conversationID);
for (int lineID = 0; lineID < 10000; lineID++)
{
//sendToReportWindow(L"lineID:%d\n", lineID);
for (int objectID = 0; objectID < 100; objectID++)
{
//sendToReportWindow(L"objectID:%d\n", objectID);
for (int tagID = 0; tagID < 50; tagID++)
{
if (conversation[conversationID][lineID][objectID][tagID] >= 0)
{
if (tagID > 0)
{
sendToReportWindow(L"conversationID:%d type:%s\n", conversationID, conversationType[conversationID].c_str());
sendToReportWindow(L"lineID:%d type:%s\n", lineID, lineType[conversationID][lineID].c_str());
sendToReportWindow(L"conversation[%d][%d][%d][%d]= %d\n", conversationID, lineID, objectID, tagID, conversation[conversationID][lineID][objectID][tagID]);
sendToReportWindow(L"\n");
}
else
{
sendToReportWindow(L"conversationID:%d type:%s\n", conversationID, conversationType[conversationID].c_str());
sendToReportWindow(L"lineID:%d type:%s\n", lineID, lineType[conversationID][lineID].c_str());
sendToReportWindow(L"conversation[%d][%d][%d][%d] text = %s\n", conversationID, lineID, objectID, tagID, text[conversation[conversationID][lineID][objectID][tagID]].c_str());
sendToReportWindow(L"\n");
}
records++;
}
}
}
}
}
sendToReportWindow(L"records:%d\n", records);
sendToReportWindow(L"\n");
// print all records on a specific branch. all lines for conversation 1, line 0
int conversationID = 1; int lineID = 0;
sendToReportWindow(L"Just print a subset of conversation:%d and Line:%d\n", conversationID, lineID);
for (int objectID = 0; objectID < 100; objectID++)
{
for (int tagID = 0; tagID < 50; tagID++)
{
if (conversation[1][0][objectID][tagID] >= 0)
{
if (tagID > 0)
{
sendToReportWindow(L"conversationID:%d type:%s\n", conversationID, conversationType[conversationID].c_str());
sendToReportWindow(L"lineID:%d type:%s\n", lineID, lineType[conversationID][lineID].c_str());
sendToReportWindow(L"conversation[%d][%d][%d][%d]= %d\n", conversationID, lineID, objectID, tagID, conversation[conversationID][lineID][objectID][tagID]);
sendToReportWindow(L"\n");
}
else
{
sendToReportWindow(L"conversationID:%d type:%s\n", conversationID, conversationType[conversationID].c_str());
sendToReportWindow(L"lineID:%d type:%s\n", lineID, lineType[conversationID][lineID].c_str());
sendToReportWindow(L"conversation[%d][%d][%d][%d] text = %s\n", conversationID, lineID, objectID, tagID, text[conversation[conversationID][lineID][objectID][tagID]].c_str());
sendToReportWindow(L"\n");
}
}
}
}
delete[] conversation; delete[] lineType;
}

Pointer to object in vector sometimes points to a different object in the vector

In the code below, the Struct1 pointer in Struct2 should point consistently to a certain object of Struct1. Each of these structs is contained in a vector.
However, when I output the index variable of the Struct1 objects pointed to in the Struct2 objects, in some cases the wrong one is returned.
Why does a pointer to an object contained in a vector sometimes point to a different object in the vector?
struct Struct1
{
size_t index;
std::vector<size_t> data;
}
struct Struct2
{
Struct1 *s1;
}
class MyClass
{
std::vector<Struct1> s1s;
std::vector<Struct2> s2s;
size_t propIndex = 0;
void input(QByteArray &line)
{
if (line == "1") {
for (size_t i = s1s.size(); i <= propIndex; i++) {
s1s.push_back({ .index = i, .data= {} });
}
QByteArrayList list = getList();
for (auto id : list) s1s.at(propIndex).data.push_back(id.toULongLong());
}
else {
if (propIndex == s2s.size()) {
s2s.push_back({ .s1 = nullptr });
}
if (line == "2") {
size_t index = getIndex();
for (size_t i = s1s.size(); i <= index; i++) {
s1s.push_back({ .index = i, .data= {} });
}
s2s.at(propIndex).s1 = &s1s.at(index);
}
}
propIndex++;
}
QByteArrayList output()
{
QByteArrayList output;
for (auto s2 : s2s) {
output += QByteArray::number(s2.s1->index) + "\n";
}
return output;
}
}
The problem is that you take a pointer to an item in a vector:
s2s.at(propIndex).s1 = &s1s.at(index);
The vector is a dynamic structure and its data may be reallocated when it grows. So any push_back() could invalidate all the pointers:
s1s.push_back({ .index = i, .data= {} });
Note that the vector allocation algorithm is designed to reserve space for several elements when it needs to grow. This explains that the issue appears only from time to time.
One solution could be to keep not a pointer but the index of the elements together with the a pointer to the vector.
As discussed in the comments on the accepted answer, a possible solution is using unique_ptrs.
In the OPs example code, the Struct1 vector would be replaced by a unique_ptr vector:
std::vector<std::unique_ptr<Struct1>> s1s;
Then the objects need to be allocated independently and a unique pointer needs to be created to store in the vector, and we need to call .get() to get a raw pointer to the object:
if (line == "1") {
for (size_t i = s1s.size(); i <= propIndex; i++) {
s1s.push_back(std::unique_ptr<Struct1>(new Struct1({ .index = i, .data = {} })));
}
QByteArrayList list = getList();
for (auto id : list) s1s.at(propIndex)->data.push_back(id.toULongLong());
}
...
if (line == "2") {
size_t index = getIndex();
for (size_t i = s1s.size(); i <= index; i++) {
s1s.push_back(std::unique_ptr<Struct1>(new Struct1({ .index = i, .data = {} })));
}
s2s.at(propIndex).s1 = s1s.at(index).get();

Return struct element from vector c++

I'm new to C++ and I'm trying to return a struct from a vector of structs by using 2 search criteria.
The function find_city is returning me everything from the defined range, regardless of whether it exists inside the vector of struct.
Here's my code:
struct cityLoc
{
int hRange;
int vRange;
int cityCode;
string cityName;
};
vector<cityLoc> cl1;
// the vector has already been preloaded with data
// function to return my struct from the vector
cityLoc find_city(int hRange, int vRange)
{
for (size_t i = 0; i < cl1.size(); i++)
{
if ((cl1[i].hRange = hRange) && (cl1[i].vRange = vRange))
{
return cl1[i];
}
}
}
int main()
{
for (int i = 0; i < 8; i++)
{
for (int j = 0; j <= 8; j++)
{
cityLoc this_city;
this_city = find_city(i, j);
cout << this_city.hRange << ", " << this_city.vRange << endl;
}
}
return 0;
}
Also, aside from this question, I was previously looking into std::find_if and didn't understand it. If I have the following code, what is the output? How do I modify it such that it returns a struct?
auto it = find_if(cl1.begin(), cl1.end(), [](cityLoc& cl) { return cl.hRange == 1; } );
You have a bug here:
if ((cl1[i].hRange = hRange) && (cl1[i].vRange = vRange))
Those = are assignments, not comparisons! Please enable compiler warnings and you won't be hurt by such obvious typos in future.
std::find_if will return the iterator to the found struct entry if it is successful, std::vector::end() otherwise. So, you should first validate the returning iterator if it is valid or not.
For example:
auto it = std::find_if( cl1.begin(), cl1.end(),
[](const cityLoc& cl) { return cl.hRange == 1; } );
if ( it == cl1.end() )
{
// ERROR: Not found! Return error code etc.
return -1;
}
// And, if found, process it here...
std::cout << it->hRange << '\n';
std::cout << it->vRange << '\n';
The criteria (predicate) part in std::find_if is a lambda expression.

Create a Lua table from a CPP string

I'm coding and artificial intelligence using Lua script. And I'd like to push my map in the Lua's stack which is stock in a std::string *. I show you :
My Lua script (just a sketch to display the map) :
function runIa(x, y, map)
table.foreach(map, print)
return 0
end
Which only displays "0" on stdout
Here is where I fill my std::string * :
int AI::update()
{
std::string *map = new std::string[2];
pos_x = 0;
pos_y = 10;
map[0] = "0101100";
map[1] = "1101001";
toot->getGlobal("runIa");
toot->pushPosToScript(pos_x, pos_y);
toot->pushMapToScript(map);
try {
toot->pcall(3, 1, 0);
}
catch (const LuaException & e){
std::cerr << e.what() << std::endl;
}
return 0;
}
And that's how I push it into the Lua's stack :
void Lua::pushMapToScript(std::string *map)
{
lua_newtable(_L);
for (unsigned int i = 0; i < 2; ++i)
{
lua_pushnumber(_L, i + 1);
lua_pushstring(_L, map[i].c_str());
lua_settable(_L, -3);
}
}
It works well for the position but not for the map. I can't display what is store in the var map in the Lua script. Does someone have any idea ?
Thanks a lot.