How to use json-spirit to read json string in C++? I read the demo code.
I find that:
const Address addrs[5] = { { 42, "East Street", "Newtown", "Essex", "England" },
{ 1, "West Street", "Hull", "Yorkshire", "England" },
{ 12, "South Road", "Aberystwyth", "Dyfed", "Wales" },
{ 45, "North Road", "Paignton", "Devon", "England" },
{ 78, "Upper Street", "Ware", "Hertfordshire", "England" } };
Can I convert a String into a json object?
char* jsonStr = "{'name', 'Tom'}";
json_spirit provides bool read_string( const String_type& s, Value_type& value ) and bool read( const std::string& s, Value& value ) to read json data from strings.
Here is an example:
string name;
string jsonStr("{\"name\":\"Tom\"}");
json_spirit::Value val;
auto success = json_spirit::read_string(jsonStr, val);
if (success) {
auto jsonObject = val.get_obj();
for (auto entry : jsonObject) {
if (entry.name_ == "name" && entry.value_.type() == json_spirit::Value_type::str_type) {
name = entry.value_.get_str();
break;
}
}
}
You could also use ifstream instead of string to read from json from file.
Please note, according to RFC4627 a string begins and ends with quotation marks.
Related
I have following JSON file
{
"list" :
{
"type" : "school",
"students" : [
{
"name" : "root",
"marks" : "100"
},
{
"name" : "joe",
"version" : "100"
}]
}
}
So in code I have parsed this JSON file and assigned this JSON object to JSON::student. So I am able to assign first element to string like this :
std::string type = student["list"]["type"];
and it works. SO similarly I want to do for "name" and "marks" in "students" array. How can I do that?
You can simply iterate over all the elements with iterators
std::string const str = "{ \"list\" : { \"type\" : \"school\", \"students\" : [ "
"{ \"name\" : \"root\", \"marks\" : \"100\"}, "
"{ \"name\" : \"joe\", \"version\" : \"100\" } "
"] } }";
nlohmann::json const js = nlohmann::json::parse(str);
nlohmann::json const& ja = js["list"]["students"];
for (auto it = ja.begin(); it != ja.end(); ++it) {
// For each individual element (root, joe, ...)
if (it->contains("name")) {
std::string const name = (*it)["name"].get<std::string>();
// Do something with the name
}
if (it->contains("marks")) {
std::string const marks = (*it)["marks"].get<std::string>();
// Do something with the marks
}
if (it->contains("version")) {
std::string const version = (*it)["version"].get<std::string>();
// Process version
}
// ...
}
I have a below json data. I am using nlohmann json in C++.
{
"CompanyName": "XYZ Tech",
"Created": "2019-10-16T20:14:29Z",
"TxnId": "4509237",
"Tags": [
{
"ROIId": "Default",
"Time": 71,
"Tracker": "emp10"
},
{
"ROIId": "MC16",
"Time": 21,
"TrackerId": "emp10"
},
{
"ROIId": "Default",
"Time": 11,
"TrackerId": "emp11"
},
{
"ROIId": "MC18",
"Time": 10,
"TrackerId": "emp11"
}
],
"Type": "TxnData"
}
In above json data, inside Tags, we have data where the ROIId is Default. I want to delete it so that the data becomes:
{
"CompanyName": "XYZ Tech",
"Created": "2019-10-16T20:14:29Z",
"TxnId": "4509237",
"Tags": [
{
"ROIId": "MC16",
"Time": 21,
"TrackerId": "emp10"
},
{
"ROIId": "MC18",
"Time": 10,
"TrackerId": "emp11"
}
],
"Type": "TxnData"
}
How can I do it in c++. Thanks
I suggest iterating through the json::array stored in Tags and saving the Key of the matched elements. This way you can later validate the deletion and safely delete the elements.
Note that deleting is exactly like erasing with a STL vector - I prefer to delete from the end of the vector to avoid changing the keys while deleting multiple elements.
Here is a quick and dirty demo
And here is the code:
#include <iostream>
#include <vector>
#include "json3.6.1.hpp"
unsigned removeDefaultROIID(nlohmann::json& jsonObject, const std::string& value) {
std::vector<int> toremove;
//Loop through the `tags` json::array and create a vector of indexes to delete:
for (auto &it : jsonObject["Tags"].items()) {
//`.get<std::string>()` is the best way to make sure we are getting the value as std::string
if (it.value().at("ROIId").get<std::string>() == value)
toremove.push_back(stoi(it.key()));
}
//sort it before erase - we want to delete first biggest index:
std::sort(toremove.rbegin(), toremove.rend());
//delete using `.erase()` method:
for (int &it : toremove)
jsonObject["Tags"].erase(jsonObject["Tags"].begin() + it);
return toremove.size();
}
int main()
{
//Create the JSON object:
nlohmann::json jsonObject = R"({"CompanyName":"XYZ Tech","Created":"2019-10-16T20:14:29Z","TxnId":"4509237","Tags":[{"ROIId": "Default","Time": 71,"Tracker": "emp10"},{"ROIId":"MC16","Time": 21,"TrackerId": "emp10"},{"ROIId":"Default","Time":11,"TrackerId":"emp11"},{"ROIId":"MC18","Time": 10,"TrackerId":"emp11"}],"Type":"TxnData"})"_json;
std::cout << "JSON nested object value conditional erase:" << std::endl;
std::cout << "JSON object TAGS count - BEFORE deletion:" << jsonObject["Tags"].size() << std::endl;
//Call the method -> jlson is passed by ref
unsigned removed = removeDefaultROIID(jsonObject, "Default");
std::cout << "JSON object TAGS count - AFTER deletion:" << jsonObject["Tags"].size() << std::endl;
return 0;
}
I am using nlohmann json to create ans save some json values. But when I look at the file, I get null values in between the json. Below is how it looks like:
{
"Created": "2019-07-03T13:54:32Z",
"DataId": "BHI-201-U8",
"Data": [
{
"Name": "Andrew",
"Attended": "Yes"
},
{
"Name": "John",
"Attended": "Yes"
},
null, <-- unexpected null
{
"Name": "Ronny",
"Attended": "No"
},
null, <-- unexpected null
null, <-- unexpected null
{
"Name": "Mathew",
"Attended": "Yes"
}
],
"Type": "Person"
}
As you can see in the above json data, I am getting some unexpected null. Below is how I am saving it:
#include "nlohmann/json.hpp"
using nlohmann::json;
int main()
{
json outJson;
//some code
string outFile = "output.json";
for (const auto &data : trackedData->Detections())
{
//some code
outJson["Data"][data.id]["Name"] = data.name;
outJson["Data"][data.id]["Attended"] = data.status;
}
outJson["Created"] = created;
outJson["DataId"] = "BHI-201-U8";
outJson["Type"] = "Person";
std::ofstream output_file(outFile);
output_file << outJson.dump(4 , ' ', false);
output_file.close();
}
How can I remove these extra null from the code.
trackedData->Detections() returns a list of objects or structures and some of them are null-valued, hence nulls in the json. Try to do null-checking before adding data entry into json.
for (const auto &data : trackedData->Detections())
{
//some code
if (data != NULL)
{
outJson["Data"][data.id]["Name"] = data.name;
outJson["Data"][data.id]["Attended"] = data.status;
}
}
This is in the documentation:
http://doc.qt.io/qt-5/qjsonobject.html#QJsonObject-1
Which shows that I can do:
QJsonObject o
{
{"x", 1},
{"y", 2}
}
...and that works, however
QJsonObject o
{
{"x", { "value", 1 } },
{"y", { "value", 2 } }
}
Does not compile. What am I missing?
You have to specify the type (QJsonObject or QJsonArray) in front of the initializer list {}, the compiler is unable to guess which one you want.
QJsonObject o
{
{"x", QJsonObject { {"key", "value"} }}
};
You could also try this approach, using C++11 raw string literal:
const QByteArray object = R"(
{
"x": { "value": "1" },
"y": { "value": "2" }
}
)";
QJsonObject jsonObject = QJsonDocument::fromJson(object).object();
qDebug() << jsonObject;
I have 3 variantMaps created from JSON and I would like to substitute
for example all 3 things from 3d with 2nd and than second to first to the properties.
QVariantMap wholeMapToChange; //1.
QVariantMap propertiesMapToChange; //2.
QVariantMap cmdMap; //3.
1st contain this JSON data but in map:
{
properties {
"A": true,
"B": true,
"fieldName": "ewfqfqewf",
"C": false,
"fieldPassword": "451541611",
"isBtnSignOnClicked": true
},
type: "xyz"
}
2nd contain this JSON data but in map:
{
"A": true,
"B": true,
"fieldName": "ewfqfqewf",
"C": false,
"fieldPassword": "451541611",
"isBtnSignOnClicked": true
}
3d contain this JSON data but in map:
{
"fieldName": "nick",
"fieldPassword": "0000",
"isBtnSignOnClicked": true
}
What I see as a possibility for substituing 3 with 2 is to create cycle
for (QVariantMap::const_iterator it = propertiesMapToChange.begin(); it != propertiesMapToChange.end(); ++it){
for (QVariantMap::const_iterator itt = cmdMap.begin(); itt != cmdMap.end(); ++itt){
///here would be the comparig...
}
}
But I dont think this is good solution... I would like to ask you for advice or opinion, whether its correct, or there exist better way to do that.
Thx
It is the right solution if the maps aren't too big, since the cost is N*M. But it is a premature pessimization. You should implement the loop to have N+M cost: after all, the maps are sorted, so you only need to iterate each map once.
A complete example:
// https://github.com/KubaO/stackoverflown/tree/master/questions/json-map-iter-39979440
#include <QtCore>
QVariantMap replaceMap(QVariantMap dst, const QVariantMap & src) {
auto dit = dst.begin();
auto sit = src.begin();
while (dit != dst.end() && sit != src.end()) {
if (sit.key() < dit.key()) {
++ sit;
continue;
}
if (dit.key() < sit.key()) {
++ dit;
continue;
}
Q_ASSERT(sit.key() == dit.key());
dit.value() = sit.value();
++ sit;
++ dit;
}
return dst;
}
int main() {
auto json1 = QJsonDocument::fromJson({R"ZZ({
"properties":{
"A":true,
"B":true,
"fieldName":"ewfqfqewf",
"C":false,
"fieldPassword":"451541611",
"isBtnSignOnClicked":true
},
"type":"xyz"
})ZZ"}).toVariant().value<QVariantMap>();
auto json2 = QJsonDocument::fromJson({R"ZZ({
"A":true,
"B":true,
"fieldName":"ewfqfqewf",
"C":false,
"fieldPassword":"451541611",
"isBtnSignOnClicked":true
})ZZ"}).toVariant().value<QVariantMap>();
auto json3 = QJsonDocument::fromJson(
{R"ZZ({
"fieldName":"nick",
"fieldPassword":"0000",
"isBtnSignOnClicked":true
})ZZ"}).toVariant().value<QVariantMap>();
json2 = replaceMap(json2, json3);
auto props = replaceMap(json1["properties"].value<QVariantMap>(), json2);
json1["properties"] = props;
qDebug() << QJsonDocument::fromVariant(json1).toJson().constData();
}
Output:
{
"properties": {
"A": true,
"B": true,
"C": false,
"fieldName": "nick",
"fieldPassword": "0000",
"isBtnSignOnClicked": true
},
"type": "xyz"
}