nlohmann json insert value array partially to already existing data - c++

I have a particular case which I am trying to solve with minimal changes if possible.
one of the data is
js["key1"]["subkey2"]["subsubkey3"].push_back({1,2,3,{4,5}});
[ 1,2,3,[[4,5]] ]
Later at some stage I want to insert
{1,2,3,{4,6}}
Then it should become
[ 1,2,3,[[4,5],[4,6]] ]
How can I make this possible without making 1,2,3 value as key?

I did some playing. I didn't get the results you were looking for. Here's my code and results so far.
#include <iostream>
#include <json.hpp>
using namespace std;
using JSON = nlohmann::json;
int main() {
JSON json = JSON::object();
JSON key1JSON = JSON::object();
JSON key2JSON = JSON::object();
JSON key3JSON = JSON::array();
key3JSON.push_back( {1,2,3, {4,5} } );
key3JSON.push_back( {6} );
key2JSON["subsubkey3"] = key3JSON;
key1JSON["subkey2"] = key2JSON;
json["key1"] = key1JSON;
cout << json.dump(2) << endl;
}
Output:
{
"key1": {
"subkey2": {
"subsubkey3": [
[
1,
2,
3,
[
4,
5
]
],
[
6
]
]
}
}
}
You'll see that the first push_back pushed an array inside an array, which is probably one level deeper than you wanted, and the second one added a second array, which is also not what you want.
Which means you're probably going to have to write your own method, especially as you want to also handle uniqueness. I personally never free-format data that way you have in your example. But maybe your method would look something like:
bool contains(const JSON &json, const JSON &value) {
... this looks like fun to write.
}
void appendUnique(JSON &json, const JSON &array) {
for (JSON & thisJson: array) {
if (!contains(json, thisJson)) {
json.push_back(thisJson);
}
}
}
I modified my code like this:
void appendUnique(JSON &json, const JSON & array) {
for (const JSON & thisJSON: array) {
json.push_back(thisJSON);
}
}
...
appendUnique(key3JSON, {1,2,3, {4,5} } );
appendUnique(key3JSON, {6} );
And got this:
{
"key1": {
"subkey2": {
"subsubkey3": [
1,
2,
3,
[
4,
5
],
6
]
}
}
}
I'm not going to write the isUnique method. But I think you may have to take this to conclusion.

Related

How to remove or erase a "key":"value" pair in a Json::Value object using "key"?

I am working in C++ language, visual studio 2022, and using jsoncpp library for working with Json.
To Give you an Idea, here is an example of Json data I am working with
[
{
"name":"Regina Eagle",
"job":"Biologist",
"salary":"728148120",
"email":"Regina_Eagle6155#y96lx.store",
"city":"Nashville"
},
{
"name":"Julius Baker",
"job":"Fabricator",
"salary":"299380360",
"email":"Julius_Baker9507#voylg.center",
"city":"Las Vegas"
},
{
"name":"Rocco Sawyer",
"job":"Chef Manager",
"salary":"223764496",
"email":"Rocco_Sawyer4620#qu9ml.club",
"city":"San Francisco"
},
{
"name":"Chad Murray",
"job":"Project Manager",
"salary":"43031808",
"email":"Chad_Murray6940#jcf8v.store",
"city":"Bridgeport"
},
{
"name":"Rocco Parker",
"job":"Lecturer",
"salary":"322089172",
"email":"Rocco_Parker202#ag5wi.solutions",
"city":"Indianapolis"
}
]
It's a Json array of objects (with key:value pairs).
I have a set of column heads for eg: {"name","job","salary"}, and I want to sort the json data in a way that each object will have only columns that are in the given set.
This is my approach:
Store json data in a Json::Value object (let us say records).
Iterate through records (as it is an array).
Creating another loop to Iterate through object stored at each index.
Extract the key of the key:value pair and check if it's present in the set or not.
If it's present then continue, else if it isn't then remove that key:value entry from there.
This way we can delete unwanted column while looping through the object.
Here, is the code snippet:
set<string> col {"name","job","salary"};
Json::Value records = [
{
"name":"Regina Eagle",
"job":"Biologist",
"salary":"728148120",
"email":"Regina_Eagle6155#y96lx.store",
"city":"Nashville"
},
{
"name":"Julius Baker",
"job":"Fabricator",
"salary":"299380360",
"email":"Julius_Baker9507#voylg.center",
"city":"Las Vegas"
},
{
"name":"Rocco Sawyer",
"job":"Chef Manager",
"salary":"223764496",
"email":"Rocco_Sawyer4620#qu9ml.club",
"city":"San Francisco"
},
{
"name":"Chad Murray",
"job":"Project Manager",
"salary":"43031808",
"email":"Chad_Murray6940#jcf8v.store",
"city":"Bridgeport"
},
{
"name":"Rocco Parker",
"job":"Lecturer",
"salary":"322089172",
"email":"Rocco_Parker202#ag5wi.solutions",
"city":"Indianapolis"
}
];
for (int i = 0; i<records.size(); i++)
{
for (auto j = records[i].begin(); j != records[i].end(); j++)
{
string key = j.key().asString();
if (col.find(key) != col.end())
{
continue;
}
else
{
records[i].removeMember(key);
}
}
}
It works fine until the 'removeMember' function get to run, and throws an error saying can't increment the value of iterator.
Expression: cannot increment value-initialized map/set iterator
Am I doing something wrong?
Or there is another/better way of doing this ?
Please advice.
Don't remove or add elements in a container you're currently iterating.
The JSON objects are stored in a std::map and removeMember calls std::map::erase. It invalidates the current iterator and it can't be incremented anymore. j++ causes the error.
One approach is to first only store the keys of properties you want to delete, and then to delete the properties in a separate loop.
set<string> col {"name","job","salary"};
Json::Value records = [
{
"name":"Regina Eagle",
"job":"Biologist",
"salary":"728148120",
"email":"Regina_Eagle6155#y96lx.store",
"city":"Nashville"
},
{
"name":"Julius Baker",
"job":"Fabricator",
"salary":"299380360",
"email":"Julius_Baker9507#voylg.center",
"city":"Las Vegas"
},
{
"name":"Rocco Sawyer",
"job":"Chef Manager",
"salary":"223764496",
"email":"Rocco_Sawyer4620#qu9ml.club",
"city":"San Francisco"
},
{
"name":"Chad Murray",
"job":"Project Manager",
"salary":"43031808",
"email":"Chad_Murray6940#jcf8v.store",
"city":"Bridgeport"
},
{
"name":"Rocco Parker",
"job":"Lecturer",
"salary":"322089172",
"email":"Rocco_Parker202#ag5wi.solutions",
"city":"Indianapolis"
}
];
for (int i = 0; i<records.size(); i++)
{
std::vector<std::string> toRemove;
for (auto j = records[i].begin(); j != records[i].end(); j++)
{
string key = j.key().asString();
if (col.find(key) != col.end())
{
continue;
}
else
{
// records[i].removeMember(key);
toRemove.push_back(key);
}
}
for (const auto &key : toRemove)
{
records[i].removeMember(key);
}
}

writing json data incremently into a file using jsoncpp

I am using jsoncpp to write the data into json format like following:
Json::Value event;
Json::Value lep(Json::arrayValue);
event["Lepton"] = lep;
lep.append(Json::Value(1));
lep.append(Json::Value(2));
lep.append(Json::Value(3));
lep.append(Json::Value(4));
event["Lepton"] = lep;
Json::StyledWriter styledWriter;
cout << styledWriter.write(event);
I got the following output:
{
"Lepton" : [
1,
2,
3,
4
]
}
I want to write multiple such blocks into my data files. What I eventually want is following:
[
{
"Lepton" : [
1,
2,
3,
4
]
},
{
"Lepton" : [
1,
2,
3,
4
]
}
]
Currently, I am writing [ and then the json entries followed by a , and finally at the end ]. Also I have to remove the last , in the final data file.
Is there a way to do all this automatically by jsoncpp or by other means?
Thanks
Using the suggestion from #Some prorammer dude in the comments section, I did the following:
Json::Value AllEvents(Json::arrayValue);
for(int entry = 1; entry < 3; ++entry)
{
Json::Value event;
Json::Value lep(Json::arrayValue);
lep.append(Json::Value(1 + entry));
lep.append(Json::Value(2 + entry));
lep.append(Json::Value(3 + entry));
lep.append(Json::Value(4 + entry));
event["Lepton"] = lep;
AllEvents.append(event);
Json::StyledWriter styledWriter;
cout << styledWriter.write(AllEvents);
}
I got the desired output as shown below:
[
{
"Lepton" : [
1,
2,
3,
4
]
},
{
"Lepton" : [
2,
3,
4,
5
]
}
]
Basically, I created a Json array and appended the resulting Json objects into it.

Convert from Rapidjson Value to Rapidjson Document

As per the tutorial:
Each JSON value is stored in a type called Value. A Document, representing the DOM, contains the root Value of the DOM tree.
If so, it should be possible to make a sub-document from a document.
If my JSON is:
{
"mydict": {
"inner dict": {
"val": 1,
"val2": 2
}
}
}
I'd like to be able to create a Document from the inner dictionary.
(And then follow the instructions in the FAQ for How to insert a document node into another document?)
Given the original document contains:
{
"mydict": {
"inner dict": {
"val": 1,
"val2": 2
}
}
}
You can copy the sub-document:
const char json[] = "{\"mydict\": {\"inner dict\": {\"val\": 1, \"val2\": 2}}}";
Document doc, sub; // Null
doc.Parse(json);
sub.CopyFrom(doc["mydict"], doc.GetAllocator());
You can also swap the sub-document with another:
const char json[] = "{\"mydict\": {\"inner dict\": {\"val\": 1, \"val2\": 2}}}";
Document doc, sub; // Null
doc.Parse(json);
sub.Swap(doc["mydict"]);
With some validation:
const char json[] = "{\"mydict\": {\"inner dict\": {\"val\": 1, \"val2\": 2}}}";
Document doc, sub; // Null
doc.Parse(json);
if (doc.IsObject()) {
auto it = doc.FindMember("mydict");
if (it != doc.MemberEnd() && it->value.IsObject()) {
sub.Swap(it->value);
}
}
Each will result in sub containing:
{
"inner dict": {
"val": 1,
"val2": 2
}
}
With the CopyFrom() approach, doc will contain:
{
"mydict": {
"inner dict": {
"val": 1,
"val2": 2
}
}
}
With Swap():
{
"mydict": null
}

Poco C++ building nested JSON objects

I have a nested JSON object. I'm trying to build it in a function and add the inner object to the original, but I can't extract the result.
void build_object (Poco::JSON::Object * const result)
{
/* Construct some int/bool/string fields here */
Poco::JSON::Object inner;
inner.set("some_number", 5);
inner.set("some_string", "xyz");
/* This is where it breaks down */
std::string key = "new_object";
result->set("new_object", inner);
/* Then some debugging and testing */
// The new object is printed inside the first -> seems like it's working
result->stringify(std::cout);
printf("result has(key): %i\n", result->has(key)); // true
printf("isObject: %i\n", result->isObject(key)); // false - huh?
printf("isNull: %i\n", result->isNull(key)); // false
printf("isArray: %i\n", result->isArray(key)); // false
Poco::JSON::Object::Ptr ptr = result->getObject(key);
// unsurpisingly fails since the above indicates it's not an object
printf("ptr isNull: %i\n", ptr.isNull()); // true
// ptr->has("some_number"); // throws NullPointerException
// if it's not an object/null/array, it must be a value
Poco::Dynamic::Var v = result->get(key);
// at least one of these things should be true, otherwise what is it?
printf("var isString: %i\n", v.isString()); // false
printf("var isStuct: %i\n", v.isStruct()); // false
printf("var isEmpty: %i\n", v.isEmpty()); // false
printf("var isArray: %i\n", v.isArray()); // false
printf("var isSigned: %i\n", v.isSigned()); // false
printf("var isNumeric: %i\n", v.isNumeric());// false
}
So, I have an inner object that is correctly put into the result, it is being printed via stringify with all the correct values and result->has() is successful. But, according to the result, it is not an object, array, or null, so you should be able to get it with var. But, once it's gotten from var, it's not a string, struct, array, or number, and it's also not empty. The inner object seems to exist and not exist at the same time.
So, how do I put this object into my result? And how do I get it out?
Thanks
note: I've seen this thread Correct usage of Poco C++ JSON for parsing data, but it's building the JSON object from string and then parsing it. I suppose I could build everything as a string and convert to the Poco Object at the last step, but I'm still curious why the above is happening. Also, using result->set() and result->get() are a cleaner, less hack-y solution than going through a string.
References: Poco JSON Doc,
Poco Dynamic Var Doc
Poco::JSON Objects and Arrays are held as shared pointers internally by default (optimization to avoid values copying) and everything is Dynamic::Var, so it works for both pointers and values. When you insert an Object as value it works because Dynamic::Var will hold pretty much anything, but the problem you experience when inspecting it comes from the fact that internal comparison does not return true for Object values because it compares only with default type - Poco::SharedPtr<Poco::JSON::Object>.
Here's a workaround:
void build_object (Poco::JSON::Object * const result)
{
// smart pointer, so don't worry about cleaning up
Poco::JSON::Object::Ptr inner = new Poco::JSON::Object;
inner->set("some_number", 5);
inner->set("some_string", "xyz");
std::string key = "new_object";
result->set(key, inner);
printf("isObject: %i\n", result->isObject(key)); // true
}
I have opened a github issue to alleviate this caveat.
I have been trying to create json file having nested object using poco library. Finally able to do with Poco::Json::Array.
Please find the posted code sinippet. Hope it will help. Json output attached with post.
#include "Poco\JSON\JSON.h"
#include "Poco\JSON\Stringifier.h"
#include "Poco\JSON\Object.h"
#include "Poco\Dynamic\Var.h"
using namespace std;
using Poco::JSON::Stringifier;
using Poco::JSON::Object;
using Poco::JSON::Array;
void makeJsonNestedObject()
{
Object RootObj(true);
Array FLArray;
for(int i=0; i<3; i++)
{
Object::Ptr FirstLevelArrayNode = new Poco::JSON::Object(true);
TCHAR strNameBuff[15];
_stprintf(strNameBuff, _T("%s_%d"),_T("Servername"),i);
std::basic_string<TCHAR> strName = strNameBuff;
FirstLevelArrayNode->set("HostName", strName);
FirstLevelArrayNode->set("Overall Impact", "Dummy Data");
Array SLArray;
for(int j=0; j<3;j++)
{
Object::Ptr SecondLevelArrayNode = new Poco::JSON::Object(true);
TCHAR attr1NameBuff[15];
TCHAR attr2NameBuff[15];
_stprintf(attr1NameBuff, _T("%s_%d"),_T("AttrOne"),j);
_stprintf(attr2NameBuff, _T("%s_%d"),_T("AttrTwo"),j);
std::basic_string<TCHAR> attr1Name = attr1NameBuff;
std::basic_string<TCHAR> attr2Name = attr2NameBuff;
SecondLevelArrayNode->set("Attribute", attr1Name);
SecondLevelArrayNode->set("SubAttribute", attr2Name);
Poco::Dynamic::Var obj(SecondLevelArrayNode);
SLArray.add(obj);
}
FirstLevelArrayNode->set("Attribute_Details",SLArray);
Poco::Dynamic::Var FLArrayNodeobj(FirstLevelArrayNode);
FLArray.add(FLArrayNodeobj);
}
std::ostringstream os;
std::cout <<"before stringlify.." << std::endl;
FLArray.stringify(os, 2);
std::cout << os.str() << std::endl;
}
Json output:
[
{
"HostName" : "Servername_0",
"Overall Impact" : "Dummy Data",
"Attribute_Details" : [
{
"Attribute" : "AttrOne_0",
"SubAttribute" : "AttrTwo_0"
},
{
"Attribute" : "AttrOne_1",
"SubAttribute" : "AttrTwo_1"
},
{
"Attribute" : "AttrOne_2",
"SubAttribute" : "AttrTwo_2"
}
]
},
{
"HostName" : "Servername_1",
"Overall Impact" : "Dummy Data",
"Attribute_Details" : [
{
"Attribute" : "AttrOne_0",
"SubAttribute" : "AttrTwo_0"
},
{
"Attribute" : "AttrOne_1",
"SubAttribute" : "AttrTwo_1"
},
{
"Attribute" : "AttrOne_2",
"SubAttribute" : "AttrTwo_2"
}
]
},
{
"HostName" : "Servername_2",
"Overall Impact" : "Dummy Data",
"Attribute_Details" : [
{
"Attribute" : "AttrOne_0",
"SubAttribute" : "AttrTwo_0"
},
{
"Attribute" : "AttrOne_1",
"SubAttribute" : "AttrTwo_1"
},
{
"Attribute" : "AttrOne_2",
"SubAttribute" : "AttrTwo_2"
}
]
}
]

Create a json array in C++

So im trying to create a json Object in c++ dynamically. I want to add a timestamp and then an array with the data included.
So thats what my json String would look like :
{
"timestep": "2160.00",
"vehicles": [
{
"id": "35092_35092_353",
"x": "6.988270",
"y": "50.872139",
"angle": "-20.812787",
"type": "passenger_P_14_1",
"speed": "0.000000",
"pos": "4.600000",
"lane": "4.600000",
"slope": "4.600000"
},
{
"id": "35092_35092_353",
"x": "6.988270",
"y": "50.872139",
"angle": "-20.812787",
"type": "passenger_P_14_1",
"speed": "0.000000",
"pos": "4.600000",
"lane": "4.600000",
"slope": "4.600000"
},
{
"id": "35092_35092_353",
"x": "6.988270",
"y": "50.872139",
"angle": "-20.812787",
"type": "passenger_P_14_1",
"speed": "0.000000",
"pos": "4.600000",
"lane": "4.600000",
"slope": "4.600000"
}
]
}
Im totally new to C++ and im using the Casablanca ( C++ REST SDK) package.
So im having a really hard time producing the code. And i cant find any working solutions. I found this on the wiki
Create a JSON object:
json::value obj;
obj[L"key1"] = json::value::boolean(false);
obj[L"key2"] = json::value::number(44);
obj[L"key3"] = json::value::number(43.6);
obj[L"key4"] = json::value::string(U("str"));
and that works for me. But how do i create an array?
i tried several things but nothing worked. Maybe theres a better package? But as far as i understood its an official micorosft package for json and http.
Help would be really nice!
There are 2 mechanisms. If you are used to std c++ libraries, this should look familiar. Element vector is derived from std::vector.
json::value::element_vector e;
// the first item in the pair is the array index, the second the value
e.push_back(std::make_pair(json::value(0), json::value(false)));
e.push_back(std::make_pair(json::value(1), json::value::string(U("hello"))));
json::value arr(e);
And, if you prefer a cleaner look, and can accept a less efficient compiled result:
json::value arr;
arr[0] = json::value(false);
arr[1] = json::value(U("hello"));
From your message you have tried a bunch of stuff. If you have tried mechanisms like these but they didn't work, give us a sample program that demontrates the failure and we'll have a crack at it.
To get the basic structure in your file above:
json::value vehicles;
vehicles[0] = // 1st vehicle object
vehicles[1] = // 2nd vehicle object
// etc
json::value root;
root[L"timestep"] = json::number(2160.0);
root[L"vehicles"] = vehicles;
Here's how to create an array dynamically using vector. Assume that you have 10 vehicles to add.
std::vector<web::json::value> arrayVehicles;
for(int i = 0; i < 10; i++)
{
web::json::value vehicle;
std::string vehicleID = "id_prefix_" + std::to_string(i);
vehicle["id"] = web::json::value::string(vehicleID);
vehicle["x"] = web::json::value::number(6.988270);
vehicle["y"] = web::json::value::number(50.872139);
arrayVehicles.push_back(vehicle);
}
web::json::value myJSON;
myJSON["vehicles"] = web::json::value::array(arrayVehicles);
You could put it like this:
json::value vehicle1;
vehicle1[L"id"] = json::value::string(L"35092_35092_353");
vehicle1[L"x"] = json::value::number(6.988270);
vehicle1[L"y"] = json::value::number(50.872139);
json::value vehicle2;
vehicle2[L"id"] = json::value::string(L"35092_35092_353");
vehicle2[L"x"] = json::value::number(1.23456);
vehicle2[L"y"] = json::value::number(6.78901);
json::value vehicles;
vehicles[L"timestamp"] = json::value::number(2160);
vehicles[L"vehicles"] = json::value::array({vehicle1, vehicle2});
Here is another method to produce a json array in Casablanca:
int size = 3;
web::json::value yourJson;
yourJson[U("vehicles")] = web::json::value::array(size);
yourJson[U("vehicles")].as_array()[0] = web::json::value(U("some entry"));
yourJson[U("vehicles")].as_array()[1] = web::json::value(U("another entry"));
//...
If you wish to use the array as an answer on a received http_request (in case below it's a http_request request), you are free to use the following snippet of code as an example:
json::value answer;
auto array = answer.array();
for (size_t i = 0; i < GenFile.GetNumberOfCurves(); i++)
{
web::json::value vehicle;
vehicle[L"smth"] = web::json::value::number(WhatEverArray[i].whatever());
array[i] = vehicle;
}
request.reply(status_codes::OK, array);
The following sample json C++ array of strings works for me.
const char * const line_items_items =
"[\
{\
\"commodity_code\": \"44121903\",\
\"description\": \"Miscellaneous goods\",\
\"upc\": \"65100004327\",\
\"quantity\": \"2\",\
\"unit_of_measurement\": \"M62\",\
\"unit_cost\": \"23.09\",\
\"discount_amount\": \"10.03\",\
\"total_amount\": \"50.03\",\
\"tax_amount\": \"10.05\",\
\"extended_amount\": \"76.04\",\
\"debit_or_credit_indicator\": \"credit\",\
\"net_or_gross_indicator\": \"net\"\
},\
{\
\"commodity_code\": \"44121809\",\
\"description\": \"Miscellaneous goods\",\
\"upc\": \"65100007654\",\
\"quantity\": \"4\",\
\"unit_of_measurement\": \"M66\",\
\"unit_cost\": \"35.09\",\
\"discount_amount\": \"5.06\",\
\"total_amount\": \"0.53\",\
\"tax_amount\": \"8.07\",\
\"extended_amount\": \"96.12\",\
\"debit_or_credit_indicator\": \"debit\",\
\"net_or_gross_indicator\": \"gross\"\
}\
]";