How to extract number from json? - c++

I want to extract a specific array from a json file:
{
"status": "OK",
"type": "startVehicle",
"info": {
"idTransit": 36612,
"timestampUTCTransit": {
"epochUTCTransitMS": 1606935562810,
"dateUTCTransit": "2020/12/2",
"hourUTCTransit": "18:59:22.810"
},
"timestampUTCReported": {
"epochUTCReportedMS": 1606935562810,
"dateUTCReported": "2020/12/2",
"hourUTCReported": "18:59:22.810"
},
"road": 0,
"lane": 0,
"xCoordinates": [
143.6,
-456.335
]
}
}
I want extract the values of xCoordinates. This is what I tried:
#include <rapidjson/document.h>
void Vdac::addStart(std::string json_str)
{
Document rjsondoc;
rjsondoc.Parse(json_str.c_str());
if(rjsondoc.HasParseError())
slog.getLogger()->debug("Invalid json");
else
{
auto coordinates = rjsondoc["info"]["xCoordinates"].GetArray();
for(SizeType i=0;i<coordinates.Size();i++)
slog.getLogger()->debug("The start coordinates are: {0:d}",coordinates[i].GetInt());
return;
}
}
But I have an error when the program accesses the value. The error is:
../include/rapidjson/document.h:1737: int rapidjson::GenericValue<Encoding, Allocator>::GetInt() const [with Encoding = rapidjson::UTF8<>; Allocator = rapidjson::MemoryPoolAllocator<>]: Assertion `data_.f.flags & kIntFlag' failed.
How can I extract the value?

Can't give you any help with rapidjson.
But an alternative is ThorsSerializer (note: I wrote it. So take that as you will). But it is designed to be easy to use. With zero (or very little) code that need to be written to parse the JSON into C++ objects.
Note: If you don't want to read a field just remove it.
#include <fstream>
#include "ThorSerialize/Traits.h"
#include "ThorSerialize/JsonThor.h"
struct TimeStamp
{
long epochUTCTransitMS;
std::string dateUTCTransit;
std::string hourUTCTransit;
};
// If you want to read a class then
// Add this declaration with the name of the class and the
// fields you want to read/write in JSON.
ThorsAnvil_MakeTrait(TimeStamp, epochUTCTransitMS, dateUTCTransit, hourUTCTransit);
struct Info
{
long idTransit;
TimeStamp timestampUTCTransit;
TimeStamp timestampUTCReported;
int road;
int lane;
std::vector<double> xCoordinates;
};
ThorsAnvil_MakeTrait(Info, idTransit, timestampUTCTransit, timestampUTCReported, road, lane, xCoordinates);
struct Object
{
std::string status;
std::string type;
Info info;
};
ThorsAnvil_MakeTrait(Object, status, type, info);
int main()
{
std::ifstream data("pl1.data");
Object object;
using ThorsAnvil::Serialize::jsonImporter;
// jsonImporter to read or jsonExporter to write:
data >> jsonImporter(object);
std::cout << object.info.xCoordinates[0] << "\n";
}
Easy to install via brew:
> brew install thors-serializer
Build your code with:
> g++ -std=c++17 <file>.cpp -lThorSerialize17 -lThorsLogging17
Note on new M1 machs you will also need -L /opt/homebrew/lib (still working on that).
All code on github

Related

How to create json using rapidjson::Writer without creating document in C++?

I am trying to create json using rapidjson::Writer without creating document but it just give json text but don't create full json output like below,
{ "data": { "dataIn": { "hello":"world", "t":true, "f":false }, "dataOut": { "n":null, "i":123, "pi":3.1416 } } }
On rapidjson documents as well not enough info given. Can please help with it. How I can achieve this?
On rapidjson documents as well not enough info given. Can please help with it. How I can achieve this? - That is not true if I correctly understood your intentions.
At RapidJSON:Sax look for paragraph Writer - example given there is almost the same as the one you're asking for.
I wrote It myself to check if it is working .. and it is:
#include <iostream>
#include <rapidjson/writer.h>
// {
// "data":
// {
// "dataIn":
// {
// "hello":"world",
// "t":true,
// "f":false
// },
// "dataOut":
// {
// "n":null,
// "i":123,
// "pi":3.1416
// }
// }
// }
int main(int argc, char** argv)
{
rapidjson::StringBuffer lStringBuffer;
rapidjson::Writer lWriter(lStringBuffer);
lWriter.StartObject();
lWriter.Key("data");
lWriter.StartObject();
lWriter.Key("dataIn");
lWriter.StartObject();
lWriter.Key("hello");
lWriter.String("world");
lWriter.Key("t");
lWriter.Bool(true);
lWriter.Key("f");
lWriter.Bool(false);
lWriter.EndObject();
lWriter.Key("dataOut");
lWriter.StartObject();
lWriter.Key("n");
lWriter.Null();
lWriter.Key("i");
lWriter.Int(123);
lWriter.Key("pi");
lWriter.Double(3.1416);
lWriter.EndObject();
lWriter.EndObject();
lWriter.EndObject();
std::cout << lStringBuffer.GetString() << std::endl;
return 0;
}
The output is:
{"data":{"dataIn":{"hello":"world","t":true,"f":false},"dataOut":{"n":null,"i":123,"pi":3.1416}}}
But if you want to get such a JSON string you might also use raw strings like:
std::string lRawString = R"(
{
"data":
{
"dataIn":
{
"hello":"world",
"t":true, "f":false
},
"dataOut":
{
"n":null,
"i":123,
"pi":3.1416
}
}
}
)";
and then pass it to rapidjson::Document:
rapidjson::Document lDocument;
lDocument.Parse(lRawString.c_str());

Error code to string? Struct to json with error_message

Im developing a program in C++ that returns info from a DLL to be used in a webpage.
The DLL returns a big struct with information but only need some fields that i plan to return as a json using https://github.com/nlohmann/json and then to char*.
Here is an example of the struct and the meaning of the values of each field (acording to the documentation pdf)
struct myStruct {
BYTE StatusCode;
BYTE ErrorCode;
DWORD WarningCode[2];
otherStruct SystemInfo[16];
...
}
StatusCode:
0x00 = No Error
0x01 = Error
0x02 = Ready
...
0x05 = Power Off
WarningCode
0x00 0x00 = No warning
0x02 0x01 = Warning Alert
... etc
Here is how i access the fields of the struct:
GetInfoStatus(&myStatusStruct);
jInfo["error_code"] = myStatusStruct.ErrorCode;
jInfo["status_code"] = myStatusStruct.StatusCode;
jInfo["warning_code"] = myStatusStruct.WarningCode2;
jInfo["is_available_warning_code"] = myStatusStruct.AvailableWarningCode2;
std::string info = jInfo.dump();
return info.c_str();
// My current return char* "json"
// {"available_warning_code":1,"error_code":255,"status_code":4}
But i would like to have something like this
{"available_warning_code": [0x01, "warning_alert"], "error_code": [0x01, "error_system_fail"], "status_code": [0x04, "low_battery"]}
Or similar so i can return also an error code to a "string" or "error_message" that indicates the meaning (a traslation) so my backend/frontend (NodeJS) later can detect "low_battery" and do something about it, instead of having to match 0x04 to a table to understand a 0x04 (that is different from other 0x04 in other key)
Ive checked this solution https://stackoverflow.com/a/208003/4620644 but still dont understand if is the best for my case and how to implement it. I have like 20 error codes, 10 warning codes, 15 status codes.
You could create a std:pair and use that in the json. Somewhere, though, you are going to have to type out all the error messages.
#include <iostream>
#include <vector>
#include <string>
#include <utility>
#include "json.h"
using namespace nlohmann;
std::pair<int, std::string> make_error(int error)
{
// Use a vector if error codes are sequential
// Otherwise, maybe a switch
std::vector<std::string> error_msgs = {
"No Error", "Error", "Ready"
};
if (error >= 0 && error < error_msgs.size()) {
return std::make_pair(error, error_msgs[error]);
}
else {
return std::make_pair(error, "Unknown");
}
}
int main()
{
json jInfo;
jInfo["error_code"] = make_error(2);
std::cout << jInfo.dump();
return 0;
}
This outputs:
{"error_code":[2,"Ready"]}
You'll have to do this for the other fields as well.
To get error string
class CodeMap {
map<pair<int, int>, string> m_warningCodes {
{make_pair(0,0), "No warning"},
{make_pair(2,1), "Warning Alert"}
};
map<int, string> m_statusCode{
{0, "No Error"},
{1, "Error"},
{2, "Ready"},
{5, "Power Off"},
};
public:
std::string GetWarningCode(int code[]){
return m_warningCodes[make_pair(code[0], code[1])];
}
std::string GetStatusCode(int code){
return m_statusCode[code];
}
};
Hexadecimal in json do not comply to RFC 7159
https://github.com/nlohmann/json/issues/249
Approach 1: Hex as string
To get hex from int type
//with c++ 20 std::format can be used instead below function
std::string GetHex(int i) {
std::stringstream stream;
stream << "0x" <<std::hex << i;
return stream.str();
}
Assign key value pair to json field
CodeMap m; //To get message string
jInfo["error_code"] = make_pair(GetHex(myStatusStruct.ErrorCode), "error_system_fail");
jInfo["status_code"] = make_pair(GetHex(myStatusStruct.StatusCode), m.GetStatusCode(myStatusStruct.StatusCode));
output:
"error_code": ["0x01", "error_system_fail"], "status_code": ["0x04", "low_battery"]
Approach 2: Hex as integer
Assign key value pair to json field
CodeMap m; //To get message string
jInfo["error_code"] = make_pair(myStatusStruct.ErrorCode, "error_system_fail");
jInfo["status_code"] = make_pair(myStatusStruct.StatusCode, m.GetStatusCode(myStatusStruct.StatusCode));
output:
"error_code": [1, "error_system_fail"], "status_code": [4, "low_battery"]

How to retrieve json value from 3 fields dynamically depends on the parameter being passed

The basic idea is to dynamically retrieve the value of 3 fields in the main function from the Config.json, namely Id, Encoding, Signature depends on the Id being passed.
First of all, in the Config.json file:
Depends on the id, different selection will be applied
e.g. if Id 509 is passed:
{
"Id": 509,
"Encoding": "NO-ENCODING",
"Signature": "X509"
}
will be applied.
Config.json
{
"SchemaIdForUsage":509,
"Schema":[
{
"Id":100,
"Encoding":"NO-ENCODING",
"Signature":"MD5"
},
{
"Id":509,
"Encoding":"NO-ENCODING",
"Signature":"X509"
}
]
}
I have used below code to parse JSON from the Config.json:
test.cpp
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int getfile()
{
string line;
ifstream myfile("E:\\config.json");
if (myfile.is_open())
{
while (myfile.good())
{
getline(myfile, line);
cout << line << endl;
}
myfile.close();
}
else cout << "Unable to open file";
system("pause");
return 0;
}
int main()
{
getfile();
return 0;
}
JSON library
https://github.com/mrtazz/restclient-cpp
Hopefully, I've made myself clear as a novice in C++.
I am wondering what's a better approach to retrieve those 3 fields value dynamically from the JSON file depends on the Id being passed. Thank you for your help.
Updated Response: using the restclient library to parse JSON
I used below code to try to retrieve JSON
std::ifstream file_input("E:\\test.txt");
Json::Reader reader;
Json::Value root;
reader.parse(file_input, root);
std::cout<<root["Id"];
Json::Value testing = root["Schema"]["Id"];
In the Debugger
I am trying to get the value of the fields Id eg :100, Encoding eg "NO-ENCODING"
There's error shows unhandled error
Any idea would be greatly appreciated. Thank you,

How to use boost property tree to parse elements from array in json string using boost?

I have a json string that looks like this:
[
"some text",
648547,
94.0,
111.0267520223,
10
]
so I need to assign a variable to each value like:
std::string value1 = "some text";
int value2 = 648547;
float value3 = 94.0;
float value4 = 111.0267520223;
int value5 = 10;
to read JSON, with Boost, I was doing something like this
std::stringstream jsonResponse;
boost::property_tree::ptree pt;
jsonResponse << "[\"some text\", 648547, 94.0, 111.0267520223, 10]";
std::istringstream is(jsonResponse);
boost::property_tree::read_json(is, pt);
But I don't know how to read array values from a property tree.
Does anyone have an idea how to do it?
thanks in advance!
Here my solution to iterate over no naming array:
boost::property_tree::basic_ptree<std::string,std::string>::const_iterator iter = pt.begin(),iterEnd = pt.end();
for(;iter != iterEnd;++iter)
{
//->first; // Key. Array elements have no names
//->second; // The object at each step
std::cout << "=> " << iter->second.get_value<std::string>() << std::endl;
}
You'll need to name the array so that it can be referenced:
{
"blah": [
"some text",
648547,
94.0,
111.0267520223,
10
]
}
This will validate on jsonlint.com, but it's still not simple to read using a property tree.
#include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/exceptions.hpp>
#include <boost/exception/diagnostic_information.hpp>
#include <boost/foreach.hpp>
typedef boost::property_tree::iptree ptree_t;
typedef ptree_t::value_type ptree_value_t;
typedef boost::optional<ptree_t &> optional_ptree_t;
void parseMyJson()
{
optional_ptree_t ptBlah = pt.get_child_optional("blah");
if (ptBlah)
{
BOOST_FOREACH (property_tree_t::value_type & field, pt.get_child("blah"))
{
}
}
}
With this kind of code you can iterate the fields in blah, but since they're different types, its not straightforward to parse.
I would suggest that you consider naming the fields so they can be directly referenced.
e.g.
field.second.get<string>("fieldname", "");
Please remember to wrap this code in a trycatch block, since boost property trees throw exceptions at the first sign of a problem (e.g. parse failure, or field not found etc.)
You might like to consider a more user friendly json library (https://github.com/nlohmann/json).

JSONCPP Writing to files

JSONCPP has a writer, but all it seems to do is get info from the parser and then output it into a string or a stream. How do I make it alter or create new objects, arrays, values, strings, et cetera and write them into the file?
#include<json/writer.h>
Code:
Json::Value event;
Json::Value vec(Json::arrayValue);
vec.append(Json::Value(1));
vec.append(Json::Value(2));
vec.append(Json::Value(3));
event["competitors"]["home"]["name"] = "Liverpool";
event["competitors"]["away"]["code"] = 89223;
event["competitors"]["away"]["name"] = "Aston Villa";
event["competitors"]["away"]["code"]=vec;
std::cout << event << std::endl;
Output:
{
"competitors" :
{
"away" :
{
"code" : [ 1, 2, 3 ],
"name" : "Aston Villa"
},
"home" :
{
"name" : "Liverpool"
}
}
}
#include <json.h>
#include <iostream>
#include <fstream>
void main()
{
std::ofstream file_id;
op_file_id.open("file.txt");
Json::Value value_obj;
//populate 'value_obj' with the objects, arrays etc.
Json::StyledWriter styledWriter;
file_id << styledWriter.write(value_obj);
file_id.close();
}
AFAICT, you create objects of type Json::Value, which caters for all the JSON data-types, and pass the result to a Json::Writer (one of its derived types, to be specific), or simply to a stream.
E.g.: to write an array of three integers to a file:
Json::Value vec(Json::arrayValue);
vec.append(Json::Value(1));
vec.append(Json::Value(2));
vec.append(Json::Value(3));
std::cout << vec;
Json::StyledWriter is deprecated, you can use Json::StreamWriterBuilder to write json into files.
Json::Value rootJsonValue;
rootJsonValue["foo"] = "bar";
Json::StreamWriterBuilder builder;
builder["commentStyle"] = "None";
builder["indentation"] = " ";
std::unique_ptr<Json::StreamWriter> writer(builder.newStreamWriter());
std::ofstream outputFileStream("/tmp/test.json");
writer -> write(rootJsonValue, &outputFileStream);
The json will be written into /tmp/test.json.
$ cat /tmp/test.json
{
"foo" : "bar"
}
First, you have to create the desired JSON::Value. You should look at all the constructors (first). To create the necessary hierarchies, see append and the operator[] overloads; there are overloads for both array indices and string keys for objects.
One way to write the JSON value back out is using StyledStreamWriter::write and ofstream.
See cegprakash's answer for how to write it.