read json message with boost json - c++

I'm trying to use boost json with property trees to decode a json message. I'm only interested about checking whether "mykey" is in the message and, if that is the case, get the corresponding values.
I'm a little lost in boost documentation, and I was trying to see what the actual code would be to parse a message such as the one below.
{
// some values
"mykey": [
{
"var1": "value1_str",
"var2" : "value2"
}
]
// some other values
}

I don't know about Boost ptree for JSON. I've tried it but it seemed... very clunky.
Here's a simple JSON parser based on the RFC, made in Spirit: https://github.com/sehe/spirit-v2-json/tree/q21356666
You could use it for your use case like test.cpp
#include <vector>
#include "json.hpp"
struct X {
static X from_json(JSON::Value const& v);
std::string var1;
double var2;
};
int main()
{
auto doc = as_object(JSON::parse(
"{\n"
" // some values\n"
" \"mykey\": [\n"
" {\n"
" \"var1\": \"value1_str\",\n"
" \"var2\" : 3.14\n"
" }\n"
" ]\n"
" // some other values\n"
"}\n"
));
if (doc.has_key("mykey"))
{
X data = X::from_json(doc["mykey"]);
std::cout << "X.var1: " << data.var1 << "\n";
std::cout << "X.var2: " << data.var2 << "\n";
}
std::cout << "doc: " << doc << "\n";
std::cout << "doc[\"mykey\"]: " << doc["mykey"] << "\n";
}
X X::from_json(JSON::Value const& v)
{
X result;
auto& o = as_object(as_array(v)[0]);
result.var1 = as_string(o["var1"]);
result.var2 = as_double(o["var2"]);
return result;
}
Output:
X.var1: value1_str
X.var2: 3.14
doc: {"mykey":[{"var1":"value1_str","var2":3.14}]}
doc["mykey"]: [{"var1":"value1_str","var2":3.14}]
There are other json libraries around. I suggest you select one to suit your needs.

Related

How to modify node element with boost::property_tree and put_value(const Type &value)

I am fairly new to boost::property_tree and I am having a little trouble with what should be a simple task.
I have a default xml file of which is to be copied and made unique with parameters passed into via the ptree & modelModifier(...) function below. All I want to do is parse the xml into a ptree, and then modify the name field (amongst others, but lets just start with name) from "default" to whatever name is passed in from the object_name variable, then write that back into the original ptree.
The function pTreeIterator just iterates through each child and displays its contents.
xml
<?xml version='1.0'?>
<sdf version='1.7'>
<model name='default'>
<link name='link'>
<inertial>
<mass>3.14999</mass>
<inertia>
<ixx>2.86712</ixx>
<ixy>0</ixy>
<ixz>0</ixz>
<iyy>2.86712</iyy>
<iyz>0</iyz>
<izz>0.524998</izz>
</inertia>
<pose>0 0 0 0 -0 0</pose>
</inertial>
</link>
</model>
</sdf>
Code
void ptreeIterator(ptree & pt)
{
using boost::property_tree::ptree;
for (auto&it : pt)
{
std::cout << __FUNCTION__ << std::endl;
std::cout << "------------------------------------------------------" << std::endl;
std::cout << "Iteration output: " << std::endl;
std::cout << "1: pt1: " << it.first << std::endl;
if(pt.get_child_optional(it.first))
{
cout << "pt.get_child_optional(it.first) ---> " << it.first << endl;
ptree pt2 = pt.get_child(it.first);
for (auto&it2 : pt2)
{
std::cout << "\t2: " << "pt2: " << it2.first << " :: " << (std::string)it2.second.data() << std::endl;
if(pt2.get_child_optional(it2.first))
{
ptree pt3 = pt2.get_child(it2.first);
for (auto&it3 : pt3)
{
std::cout << "\t\t3: " << "pt3: " << it3.first << " :: " << (std::string)it3.second.data() << std::endl;
}
}
}
}
}
}
ptree & modelModifier(ptree &model, double px, double py, std::string object_name, uint16_t height)
{
for(auto &it:model){
cout << "it.first = " << it.first << endl;
if(it.first=="model"){
cout << "MODEL TAG" << endl;
model.put_value(object_name);
}
ptreeIterator(model);
}
}
int main(){
ptree ptModel;
const string filenameToInsert = "model.sdf";
std::ifstream ifsModel(filenameToInsert,std::ifstream::binary);
read_xml(ifsModel, ptModel, boost::property_tree::xml_parser::trim_whitespace);
modelModifier(ptModel, 0, 0, "TEST1234567", 30);
return 0;
}
Output
it.first = model
it.second.data()
ptreeIterator
------------------------------------------------------
Iteration output:
1: pt1: model
pt.get_child_optional(it.first) ---> model
2: pt2: <xmlattr> ::
3: pt3: name :: default
Expected Output
it.first = model
it.second.data()
ptreeIterator
------------------------------------------------------
Iteration output:
1: pt1: model
pt.get_child_optional(it.first) ---> model
2: pt2: <xmlattr> ::
3: pt3: name :: TEST1234567
Firstly, your code has UB, since modelModifier doesn't return a value.
The C-style cast in (std::string)it2.second.data() is extremely dangerous as it risks reinterpret_cast-ing unrelated types. There is no reason whatsoever for this kind of blunt casting. Just remove the cast!
Also, ptreeIterator should probably take a ptree const&, not ptree&.
With these fixed, the sample does NOT show the output you claim, instead it prints (Live On Coliru)
it.first = sdf
ptreeIterator
------------------------------------------------------
Iteration output:
1: pt1: sdf
pt.get_child_optional(it.first) ---> sdf
2: pt2: <xmlattr> ::
3: pt3: version :: 1.7
2: pt2: model ::
3: pt3: <xmlattr> ::
3: pt3: link ::
Now even in your question output, you clearly see the difference between the model node and its name attribute, which apparently you want to modify. Just write the code to access that:
it.second.get_child("<xmlattr>.name").put_value(object_name);
This would be correct, assuming that the attribute always exists and instead of ptModel you pass ptModel.get_child("sdf") to modifyModel).
Other Notes: SIMPLIFY!
That said, please simplify the whole thing!
ptree pt2 = pt.get_child(it.first);
Should have been something like
ptree const& pt2 = it.second;
And
the use of get_child_optional only to repeat with get_child is even more wasteful
Good practice is to separate output/query and mutation. So don't call ptreeIterator from inside modelModifier
Also, give functions a good descriptive name (so that you don't have sheepishly explain "The function pTreeIterator just iterates through each child and displays its contents" - just call it displayModel?)
Instead of painstakingly (and flawed) iterating the particular model and printing it in pretty confusing bespoke manner, just use write_xml/write_info/write_json to dump it in a reliable manner.
Listing
Live On Coliru
namespace Model {
void display(ptree const& pt)
{
write_json(std::cout << __FUNCTION__ << "\n---------------\n", pt);
}
void modify(ptree& model, double, double, std::string object_name, uint16_t)
{
for (auto& it : model) {
std::cout << "root = " << it.first << std::endl;
it.second.get_child("model.<xmlattr>.name").put_value(object_name);
}
}
}
Prints
root = sdf
display
---------------
{
"sdf": {
"<xmlattr>": {
"version": "1.7"
},
"model": {
"<xmlattr>": {
"name": "TEST1234567"
},
"link": {
"<xmlattr>": {
"name": "link"
},
"inertial": {
"mass": "3.14999",
"inertia": {
"ixx": "2.86712",
"ixy": "0",
"ixz": "0",
"iyy": "2.86712",
"iyz": "0",
"izz": "0.524998"
},
"pose": "0 0 0 0 -0 0"
}
}
}
}
}
Bonus
In the case that the name attribute might not already exist, the following code would create it on the fly:
void modify(ptree& model, double, double, std::string object_name, uint16_t)
{
ptree value;
value.put_value(object_name);
for (auto& it : model) {
std::cout << "root = " << it.first << std::endl;
it.second.put_child("model.<xmlattr>.name", value);
}
}

Converting a json value that keeps changing to int in c++

Okay so i get this JSON object from my client:
{"command":"BrugerIndtastTF","brugerT":"\"10\"","brugerF":"\"20\""}
Then i need to use the int value from "brugerT", but as you can see it has "\"10\"" around it. When i code this in javascript i dont get this problem. Is there a way to only use the part of "brugerT" that says 10?
the code where *temp only should print the int value 10:
socket_->hub_.onMessage([this](
uWS::WebSocket<uWS::SERVER> *ws,
char* message,
size_t length,
uWS::OpCode opCode
)
{
std::string data = std::string(message,length);
std::cout << "web::Server:\t Data received: " << data << std::endl;
// handle manual settings
std::cout << "Web::Server:\t Received request: manual. Redirecting message." << std::endl;
json test1 = json::parse(data);
auto test2 = test1.json::find("command");
std::cout << "Web::Server:\t Test 1" << test1 << std::endl;
std::cout << "Web::Server:\t Test 2" << *test2 << std::endl;
if (*test2 =="BrugerIndtastTF")
{
std::cout<<"Web::Server:\t BrugerIndtastTF modtaget" << std::endl;
auto temp= test1.json::find("brugerT");
auto humi= test1.json::find("brugerF");
std::cout << "Web::Server:\t temp: " << *temp << "humi: " << *humi << std::endl;
}
});
EDIT:
Here you can see the terminal
it should just say: temp: 10 humi: 20
You can try to get the string value of brugerT and strip the \" out of the string and then convert the resulting string into a int with stoi. You could even use a regular expression to find the integer inside the string and let that library figure out what is the best matching method. A regular expression for that would be something like: ([0-9]+)
ps string literal type 6 might be of some use when manually filtering out \"
#include <iostream>
#include <regex>
#include <string>
using namespace std;
int main() {
string inputStr(R"("\"10\"")");
regex matchStr(R"(([0-9]+))");
auto matchesBegin = sregex_iterator(inputStr.begin(), inputStr.end(), matchStr);
auto matchesEnd = sregex_iterator();
for (sregex_iterator i = matchesBegin; i != matchesEnd; ++i) {
cout << i->str() << endl;
}
return 0;
}

How to iterate over a JSON in JSON for modern c++

I would like to iterate over each of entries in a json object, but I am getting one incomprehensible error after the other. How to correct the following example?
#include <iostream>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
void bla(std::string a) {
std::cout << a << '\n';
}
int main() {
json RecentFiles;
RecentFiles["1"]["Name"] = "test1.txt";
RecentFiles["1"]["Last modified"] = "monday";
RecentFiles["1"]["Score"] = 5.0f;
RecentFiles["2"]["Name"] = "test2.txt";
RecentFiles["2"]["Last modified"] = "tuesday";
RecentFiles["2"]["Score"] = 5.0f;
for (auto it = RecentFiles.begin(); it != RecentFiles.end(); ++it) {
bla("JSON: Recent file = " + it.value()["Name"]);
}
std::cout << RecentFiles; }
Error:
prog.cc: In function 'int main()':
prog.cc:18:31: error: invalid conversion from 'const char*' to 'nlohmann::detail::iter_impl<nlohmann::basic_json<> >::difference_type {aka long int}' [-fpermissive]
std::cout << it["Name"];
^
In file included from prog.cc:2:0:
./nlohmann/json.hpp:4418:15: note: initializing argument 1 of 'nlohmann::detail::iter_impl<BasicJsonType>::reference nlohmann::detail::iter_impl<BasicJsonType>::operator[](nlohmann::detail::iter_impl<BasicJsonType>::difference_type) const [with BasicJsonType = nlohmann::basic_json<>; nlohmann::detail::iter_impl<BasicJsonType>::reference = nlohmann::basic_json<>&; nlohmann::detail::iter_impl<BasicJsonType>::difference_type = long int]'
reference operator[](difference_type n) const
^
The above is done in the sandbox
https://wandbox.org/permlink/LNck7Gktm14bmPy0
This is not actual code that I am using, I just want to see if I can understand how to do the various basic things that I need to do with JSON.
Currently I understand so little, that I do not know if what I am doing is essentially correct but just breaks due to something stupid, or if I am doing something fundamentally wrong.
The nlohmann json library promotes itself as "JSON for modern C++" and aspires to behave "just like an STL container". There is, however, no container in the C++ standard library that is both "vector-like" and "map-like", and that supports both begin/end iterators over values, and begin/end iterators over key/value pairs. So something new is needed.
nlohmann's original solution was to copy jsoncpp's approach, which supports begin/end iterators for json arrays, and adds a distinctly unstandard key() function to the iterator to also support json objects. So you could write
for (auto it = RecentFiles.begin(); it != RecentFiles.end(); ++it)
{
std::cout << it.key() << "\n";
std::cout << (*it)["Name"].get<std::string>() << "\n";
std::cout << (*it)["Last modified"].get<std::string>() << "\n";
}
But being an unstandard way of iterating over key/values, this doesn't have standard library support for range based for loops over key/values.
nlohmann later added the json::items() function that does support iteration over json objects with standard iterators, and which does have standard library support for range based for loops, viz.
int main()
{
json RecentFiles;
RecentFiles["1"]["Name"] = "test1.txt";
RecentFiles["1"]["Last modified"] = "monday";
RecentFiles["1"]["Score"] = 5.0f;
RecentFiles["2"]["Name"] = "test2.txt";
RecentFiles["2"]["Last modified"] = "tuesday";
RecentFiles["2"]["Score"] = 5.0f;
for (const auto& item : RecentFiles.items())
{
std::cout << item.key() << "\n";
for (const auto& val : item.value().items())
{
std::cout << " " << val.key() << ": " << val.value() << "\n";
}
}
std::cout << "\nor\n\n";
for (const auto& item : RecentFiles.items())
{
std::cout << item.key() << "\n";
std::cout << " " << item.value()["Name"].get<std::string>() << "\n";
std::cout << " " << item.value()["Last modified"].get<std::string>() << "\n";
std::cout << " " << item.value()["Score"].get<double>() << "\n";
}
}
Output:
1
Last modified: "monday"
Name: "test1.txt"
Score: 5.0
2
Last modified: "tuesday"
Name: "test2.txt"
Score: 5.0
or
1
test1.txt
monday
5
2
test2.txt
tuesday
5

Way containing NodeRefs with invalid location

I'm parsing the mayotte pbf with osmium, and my handler is looking for ways. When I find one I process its barycentre and print it. The issue I ran into is that all of the ways I process have invalid location. And if print the location I get undefined for both latitude and longitude.
Is there an issue with my PBF file, or with my understanding of the osmium library?
Here is a mcve:
/**
* To compile this script, you should first install `libosmium` and its
* dependencies. Then:
* g++ -std=c++11 -lz -lexpat -lbz2 mcve.cpp -o mcve
*/
#include <iostream>
#include <osmium/handler.hpp>
#include <osmium/io/any_input.hpp>
#include <osmium/osm/node.hpp>
#include <osmium/osm/way.hpp>
#include <osmium/visitor.hpp>
class ParkingAndCarpoolingAreasHandler : public osmium::handler::Handler {
public:
void way(const osmium::Way& way) {
double lng;
double lat;
double count = 0.0;
for (const osmium::NodeRef& nr : way.nodes()) {
if (!nr.location().valid()) {
std::cerr << "Way (id=" << way.id()
<< " version=" << way.version()
<< " timestamp=" << way.timestamp()
<< " visible=" << (way.visible() ? "true" : "false")
<< " changeset=" << way.changeset()
<< " uid=" << way.uid()
<< " user=" << way.user() << ")\n";
std::cerr << "NodeRef (ref=" << nr.ref() << " location=" << nr.location() << ")\n";
std::cerr << std::endl;
return;
}
count++;
lng += nr.location().lon();
lat += nr.location().lat();
}
lng /= count;
lat /= count;
std::cout << "POINT(" << lat << ' ' << lng << ")\n";
}
};
int main() {
auto otypes = osmium::osm_entity_bits::node | osmium::osm_entity_bits::way;
osmium::io::Reader reader{"tmp/mayotte-latest.osm.pbf", otypes};
ParkingAndCarpoolingAreasHandler handler;
osmium::apply(reader, handler);
reader.close();
}
In OSM a way typically stores only references to the node it consists of. These references just contain the node ID but no additional information (such as coordinates and tags). To obtain node coordinates you have to look at the actual nodes, not just at their reference.
See OSM XML and PBF Format for more information.
Since I have no experience with osmium I can't tell you how to retrieve the corresponding nodes by their IDs. However according to the Osmium Concepts Manual you can use a NodeLocationsForWays handler to populate your NodeRef objects with locations. examples/osmium_road_length.cpp contains an example.

Correct usage of Poco C++ JSON for parsing data

Can anyone instruct me on how the Poco C++ JSON works?
Previously I've used JsonReader and JsonToken. The Poco C++ library doesn't seem to have corresponding objects.
How do I for example use the json parser to create a object name consisting the JSON value at the tag name?
EDIT: as of 1.5.2, things were simplified by making DefaultHandler, well ... default (and renaming it to its proper name - ParseHandler. So, if all you need is parsing, no need to explicitly provide the handler anymore:
// objects
std::string json = "{ \"test\" : { \"property\" : \"value\" } }";
Parser parser;
Var result = parser.parse(json);
Object::Ptr object = result.extract<Object::Ptr>();
Var test = object->get("test");
object = test.extract<Object::Ptr>();
test = object->get("property");
std::string value = test.convert<std::string>();
// array of objects
std::string json = "[ {\"test\" : 0}, { \"test1\" : [1, 2, 3], \"test2\" : 4 } ]";
Parser parser;
Var result = parser.parse(json);
Array::Ptr arr = result.extract<Array::Ptr>();
Object::Ptr object = arr->getObject(0);//
assert (object->getValue<int>("test") == 0);
object = arr->getObject(1);
arr = object->getArray("test1");
result = arr->get(0);
assert (result == 1);
See this answer for more details.
#include <iostream>
#include <string>
#include <Poco/JSON/JSON.h>
#include <Poco/JSON/Parser.h>
#include <Poco/Dynamic/Var.h>
using namespace std;
using namespace Poco::JSON;
string GetValue(Object::Ptr aoJsonObject, const char *aszKey) {
Poco::Dynamic::Var loVariable;
string lsReturn;
string lsKey(aszKey);
// Get the member Variable
//
loVariable = aoJsonObject->get(lsKey);
// Get the Value from the Variable
//
lsReturn = loVariable.convert<std::string>();
return lsReturn;
}
int main(int argc, char *argv[]) {
string lsJson;
Parser loParser;
lsJson = "{\"TransactionCode\":\"000000\",\"FileRecordSequenceNumber\":\"111111\",\"TcrSequenceNumber\":\"222222\",\"TransactionRouteIndicator\":\"ABCDE\",\"MerchantEstablishmentNumber\":\"00000000000\",\"MerchantName\":\"BBBBBBBBB\",\"MerchantCity\":\"CCCCCCCC\"}";
cout << lsJson << endl;
// Parse the JSON and get the Results
//
Poco::Dynamic::Var loParsedJson = loParser.parse(lsJson);
Poco::Dynamic::Var loParsedJsonResult = loParser.result();
// Get the JSON Object
//
Object::Ptr loJsonObject = loParsedJsonResult.extract<Object::Ptr>();
// Get the values for the member variables
//
//
cout << "TransactionCode " << GetValue(loJsonObject, "TransactionCode") << endl;
cout << "FileRecordSequenceNumber " << GetValue(loJsonObject, "FileRecordSequenceNumber") << endl;
cout << "TcrSequenceNumber " << GetValue(loJsonObject, "TcrSequenceNumber") << endl;
cout << "TransactionRouteIndicator " << GetValue(loJsonObject, "TransactionRouteIndicator") << endl;
cout << "MerchantEstablishmentNumber " << GetValue(loJsonObject, "MerchantEstablishmentNumber") << endl;
cout << "MerchantName " << GetValue(loJsonObject, "MerchantName") << endl;
cout << "MerchantCity " << GetValue(loJsonObject, "MerchantCity") << endl;
return 0;
}
Results:
{"TransactionCode":"000000","FileRecordSequenceNumber":"111111","TcrSequenceNumber":"222222","TransactionRouteIndicator":"ABCDE","MerchantEstablishmentNumber":"00000000000","MerchantName":"BBBBBBBBB","MerchantCity":"CCCCCCCC"}
TransactionCode 000000
FileRecordSequenceNumber 111111
TcrSequenceNumber 222222
TransactionRouteIndicator ABCDE
MerchantEstablishmentNumber 00000000000
MerchantName BBBBBBBBB
MerchantCity CCCCCCCC