Error code to string? Struct to json with error_message - c++

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"]

Related

How can I prevent boost log to duplicate message

I have code where I log different categories of information. Each piece of code has a specific tag that has been made for them. (e.g : database.message, database.odbc, event.cevent ...)
Moreover, I've made a function that reads a json file and their value which corresponds to their according severity filter
{
"database" :
{
"message": "INFO",
"odbc": "DEBUG",
},
"event" :
{
"cevent" : "INFO",
},
}
My problem is, I want to set a "basic filter", (for instance only log messages that are "INFO" or above) for all items whose tags were not set in this file.
Right now, I'm adding filters this way:
logging::core::get()->add_global_attribute("Tag", attrs::constant<std::string>(""));
logging::add_common_attributes();
std::vector<std::string> tags; // Suppose it's already filled with the tags and values from the json
...
for (const auto& tag :tags)
{
boost::shared_ptr<text_sink> sink(new text_sink(backend)); // same as the doc
auto level = logging::trivial::info; // just an example for more clarity
sink->set_filter(expr::attr<std::string>("Tag") == tag && expr::attr<logging::trivial::severity_level>("Severity") >= level);
logging::core::get()->add_sink(sink);
}
This piece of code works and correctly reads and sets filter according to the json file.
So to add this "basic filter", I also added this once every filter has been set:
boost::shared_ptr<text_sink> basic_sink(new text_sink(backend));
auto filter = logging::trivial::severity >= logging::trivial::info;
for (const auto& tag : tags)
{
filter = filter && expr::attr<std::string>("Tag") != tag;
}
basic_sink->set_filter(filter);
logging::core::get()->add_sink(basic_sink);
But it duplicates messages that are defined in the json, when I thought this would filter out tags stored. Do you have any ideas on how to avoid such duplication or do I have to implement such a sink as mentionned in this post
You weren't duplicating messages. You were adding duplicate sinks.
As someone else posted, you want to combine into one filter instead of duplicating your sinks.
However, since the filter expression is a compile-time static template expression that describes a deferred invocation, you need a deferred function to work with it.
I'd use boost::phoenix::function to make it simple:
boost::phoenix::function matching = [tags](logging::value_ref<std::string> actual) {
return tags.contains(actual.get());
};
Now you can use the single filter expression:
auto filter = matching(_tag) && (logging::trivial::severity >= level);
C++ 11
The above assumed C++17, but you can spell it out for older beasts:
struct match_impl {
std::set<std::string> target_tags;
using result_type = bool;
result_type operator()(logging::value_ref<std::string> actual_tag) const {
return 0 < target_tags.count(actual_tag.get());
}
};
boost::phoenix::function<match_impl> matching{match_impl{std::move(tags)}};
If Boost Log supports C++03 I know how to get that done too, but hopefully that's not required.
Full Demo
Live On Coliru
#include <boost/log/sinks/text_file_backend.hpp>
#include <boost/log/sources/logger.hpp>
#include <boost/log/attributes/scoped_attribute.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/utility/setup.hpp>
#include <boost/phoenix.hpp>
#include <set>
namespace logging = boost::log;
namespace sinks = logging::sinks;
namespace attrs = logging::attributes;
void init_logging(logging::trivial::severity_level level, std::set<std::string> tags) {
auto core = logging::core::get();
using text_sink = sinks::synchronous_sink<sinks::text_ostream_backend>;
core->add_global_attribute("Tag", attrs::constant<std::string>(""));
logging::add_common_attributes();
auto backend = boost::make_shared<sinks::text_ostream_backend>();
auto sink = boost::make_shared<text_sink>(backend);
sink->locked_backend()->add_stream(
boost::shared_ptr<std::ostream>(&std::clog, boost::null_deleter{}));
#if __cplusplus < 201703L
struct match_impl {
std::set<std::string> target_tags;
using result_type = bool;
result_type operator()(logging::value_ref<std::string> actual_tag) const {
return 0 < target_tags.count(actual_tag.get());
}
};
boost::phoenix::function<match_impl> matching{match_impl{std::move(tags)}};
#else
boost::phoenix::function matching = [tags](logging::value_ref<std::string> actual) {
return tags.contains(actual.get());
};
#endif
auto _tag = boost::log::expressions::attr<std::string>("Tag");
auto filter = matching(_tag) && (logging::trivial::severity >= level);
sink->set_filter(filter);
core->add_sink(sink);
}
int main() {
init_logging(logging::trivial::severity_level::error, {"foo", "bar", "qux"});
for (std::string tag : {"foo", "bogus", "bar"}) {
BOOST_LOG_SCOPED_THREAD_ATTR("Tag", attrs::constant<std::string>(tag));
BOOST_LOG_TRIVIAL(debug) << "debug tagged with " << tag;
BOOST_LOG_TRIVIAL(error) << "error tagged with " << tag;
BOOST_LOG_TRIVIAL(fatal) << "fatal tagged with " << tag;
BOOST_LOG_TRIVIAL(info) << "info tagged with " << tag;
BOOST_LOG_TRIVIAL(trace) << "trace tagged with " << tag;
BOOST_LOG_TRIVIAL(warning) << "warning tagged with " << tag;
}
}
Prints the expected
error tagged with foo
fatal tagged with foo
error tagged with bar
fatal tagged with bar

Write in JSON file - Data not inserted in the correct order

I am creating a desktop app using QT C++ that take a text file and convert it to JSON File like this example:
{
"102": {
"NEUTRAL": {
"blend": "100"
},
"AE": {
"blend": "100"
}
},
"105": {
"AE": {
"blend": "100"
},
"NEUTRAL": {
"blend": "100"
}
}
}
This is the code I am using:
for (int i = 0; i < output_list1.size(); i++) {
if (output_list1[i] == "-") {
c_frame++;
continue;
}
if (output_list1[i] != "NEUTRAL") {
QJsonObject neutralBlendObject;
neutralBlendObject.insert("blend", "100");
QJsonObject phonemeObject;
phonemeObject.insert("NEUTRAL", neutralBlendObject);
QJsonObject keyBlendObject;
keyBlendObject.insert("blend", output_list1[i].split(' ')[1]);
phonemeObject.insert(output_list1[i].split(' ')[0], keyBlendObject);
mainObject.insert(QString::number(c_frame), phonemeObject);
}
c_frame++;
}
jsonDoc.setObject(mainObject);
file.write(jsonDoc.toJson());
file.close();
As you can see, I am inserting the NEUTRAL object first but I am getting data not in the correct order, somtimes NEUTRAL is the the first following with the next object and somtimes not.
How can I correct this issue?
In JSON there are two structures you can use for saving data:
1.) JSON Objects: A collection of key/value pairs. This collection is NOT ordered. In various languages its realized via associative data structures like dictionaries, hash tables etc. Internally qt saves JSON objects via QHash<QString, QVariant> containers.
2.) JSON Arrays: An ordered list of values (which can be JSON Objects). In programming languages this is realized as an array/vector/whatever. Qt uses a QVariantList (QList<QVariant) internally.
I did resolve it using rapidjson library instead of QJsonObject
Based on this example
#include <rapidjson/document.h>
#include <rapidjson/writer.h>
#include <rapidjson/stringbuffer.h>
#include "rapidjson/filewritestream.h"
#include <string>
#include "RapidJson/prettywriter.h"
using namespace rapidjson;
using namespace std;
FILE* fp = fopen("output.json", "wb"); // non-Windows use "w"
char writeBuffer[65536];
FileWriteStream os(fp, writeBuffer, sizeof(writeBuffer));
Document d;
d.SetObject();
rapidjson::Document::AllocatorType& allocator = d.GetAllocator();
size_t sz = allocator.Size();
d.AddMember("version", 1, allocator);
d.AddMember("testId", 2, allocator);
d.AddMember("group", 3, allocator);
d.AddMember("order", 4, allocator);
Value tests(kArrayType);
Value obj(kObjectType);
Value val(kObjectType);
string description = "a description";
//const char *description = "a description";
//val.SetString()
val.SetString(description.c_str(), static_cast<SizeType>(description.length()), allocator);
obj.AddMember("description", val, allocator);
string help = "some help";
val.SetString(help.c_str(), static_cast<SizeType>(help.length()), allocator);
obj.AddMember("help", val, allocator);
string workgroup = "a workgroup";
val.SetString(workgroup.c_str(), static_cast<SizeType>(workgroup.length()), allocator);
obj.AddMember("workgroup", val, allocator);
val.SetBool(true);
obj.AddMember("online", val, allocator);
tests.PushBack(obj, allocator);
d.AddMember("tests", tests, allocator);
// Convert JSON document to string
PrettyWriter<FileWriteStream> writer(os);
char indent = ' '; // single space x 4
writer.SetIndent(indent, 4);
d.Accept(writer);
Thank you all for your time

Error on 'nlohmann::json::from_bson()': syntax error while parsing BSON cstring

I have a binary std::string (length = 560):
01111011001000100111010101110011011001010111001001101110011000010110110101100101001000100011101000100000001000100100110001101001011011100110111101111001001000100010110000100000001000100111000001100001011100110111001101110111011011110111001001100100001000100011101000100000001000100011000100110010001100110011010000110101001101100010001000101100001000000010001001100101011011010110000101101001011011000010001000111010001000000010001001101100011010010110111001101111011110010100000001101101011000010110100101101100001011100110001101101111011011010010001001111101
When I convert it to ASCII (using online websites), I get the JSON string I want:
{"username": "Linoy", "password": "123456", "email": "linoy#mail.com"}
I want to convert the binary std::string to a nlohmann::json object, so I use nlohmann::json::from_bson().
To do it, I need to send to from_bson() function a std::vector<uint8_t>
So I convert the std::string to the needed std::vector<uint8_t>:
using JSON = nlohmann::json;
std::vector<uint8_t> vec;
vec.assign(to_convert.begin(), to_convert.end());
JSON json_object = JSON::from_bson(vec);
The problem is I get an error on the when calling from_bson(), is that I get an error:
[json.exception.parse_error.110] parse error at byte 561: syntax error while parsing BSON cstring: unexpected end of input
**That's it. That's my problem. Can someone help me?**
**If you wanna see where is the exact error, follow this:**
I tried to place some breakpoints, and found out that the code is throwing an exception in json.hpp: line 24338, sax_parse() function (line 10 in this codeblock):
template<typename InputType>
JSON_HEDLEY_WARN_UNUSED_RESULT
static basic_json from_bson(InputType&& i,
const bool strict = true,
const bool allow_exceptions = true)
{
basic_json result;
detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
auto ia = detail::input_adapter(std::forward<InputType>(i));
const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::bson, &sdp, strict);
return res ? result : basic_json(value_t::discarded);
}
When going deeper, inside sax_parse() function, it throw the exception in json.hpp: line 7743, parse_bson_internal() function (line 10 in this codeblock):
bool sax_parse(const input_format_t format,
json_sax_t* sax_,
const bool strict = true,
const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
{
sax = sax_;
bool result = false;
switch (format)
{
case input_format_t::bson:
result = parse_bson_internal();
break;
When going deeper, inside parse_bson_internal(), it throw the exception in json.hpp: line 7803, pars_bson_element_list(false) function (line 9 in this codeblock):
bool parse_bson_internal()
{
std::int32_t document_size{};
get_number<std::int32_t, true>(input_format_t::bson, document_size);
if (JSON_HEDLEY_UNLIKELY(!sax->start_object(std::size_t(-1))))
{
return false;
}
if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_list(/*is_array*/false)))
{
return false;
}
return sax->end_object();
}

How to extract number from json?

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

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).