I have to read some data from a JSON file using C++. After some research I found Boost Property Tree as the most suggested approach and I was able to get it up and running with simple strings. However, when it comes to deep, nested arrays and matrices, I have found no solution that is working for me.
This is what my JSON file looks like. There can be multiple "objects" with their data each:
{
"some_data":
{
(...)
},
"objects": [
{
"name": "Some name",
"id": 0,
"array": [ 1.9352999925613403, -1.0619000196456909, 38.685501098632813 ],
"matrix": [
[ -0.74119997024536133, -0.56959998607635498, 0.35519999265670776, 0 ],
[ 0.61210000514984131, -0.35649999976158142, 0.70579999685287476, 0 ],
[ 0.27540001273155212, -0.74059998989105225, -0.6129000186920166, 0 ],
[ 1.9352999925613403, -1.0619000196456909, 38.685501098632813, 1 ]
],
(...)
},
(...)
]
}
I use the following code to successfully retrieve data stored in the string "name" (both loops work just fine):
#include <iostream>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/foreach.hpp>
int main() {
boost::property_tree::ptree root;
boost::property_tree::read_json("dataFile.json", root);
std::string name;
BOOST_FOREACH(boost::property_tree::ptree::value_type& v, root.get_child("objects")) {
name = v.second.get<std::string>("name");
}
for(boost::property_tree::ptree::value_type& v : root.get_child("objects")) {
name = v.second.get<std::string>("name");
}
return 0;
}
Is there a similar approach or a little extension to my code that allows me to get the values of the array and matrix? I was not able to nest another for(each) loop inside the one iterating through "objects" but could not get the array or matrix data at once either.
Thank you for any help in advance!
Like I warn in my comment, Boost Property Tree is not a JSON library. This doesn't mean that what you ask for here is impossible.
It is possible. Let's pick Array
and Matrix
definitions:
using Array = std::array<double, 3>;
using Matrix = std::array<std::array<double, 4>, 4>;
My preferred strategy (instead of using the Translators framework from Property Tree), is to write procedural helpers:
void read(ptree const& pt, double& into);
void read(ptree const& pt, Array& into);
void read(ptree const& pt, Matrix& into);
In fact, we can catch both Array/Matrix in the same implementation:
void read(ptree const& pt, double& into) {
into = pt.get_value<double>();
}
template <typename ArrayOrMatrix>
void read(ptree const& pt, ArrayOrMatrix& into) {
auto elements = pt.equal_range(""); // unnamed elements
assert(boost::size(elements) == into.size());
auto out = into.begin();
for (auto& [_,v] : boost::make_iterator_range(elements)) {
read(v, *out++);
}
}
Now we can simply write our program as:
boost::property_tree::ptree pt;
read_json(iss, pt);
for (auto& [_,obj] : pt.get_child("objects")) {
std::cout << "Reading object " << std::quoted(obj.get<std::string>("name")) << "\n";
Array a;
Matrix m;
read(obj.get_child("array"), a);
read(obj.get_child("matrix"), m);
}
See it Live On Coliru
#include <boost/property_tree/json_parser.hpp>
#include <iostream>
#include <iomanip>
#include <cassert>
using boost::property_tree::ptree;
using Array = std::array<double, 3>;
using Matrix = std::array<std::array<double, 4>, 4>;
void read(ptree const& pt, double& into) {
into = pt.get_value<double>();
}
template <typename ArrayOrMatrix>
void read(ptree const& pt, ArrayOrMatrix& into) {
auto elements = pt.equal_range(""); // unnamed elements
assert(boost::size(elements) == into.size());
auto out = into.begin();
for (auto& [_,v] : boost::make_iterator_range(elements)) {
read(v, *out++);
}
}
int main() {
std::istringstream iss(R"({
"some_data":
{
},
"objects": [
{
"name": "Some name",
"id": 0,
"array": [ 1.9352999925613403, -1.0619000196456909, 38.685501098632813 ],
"matrix": [
[ -0.74119997024536133, -0.56959998607635498, 0.35519999265670776, 0 ],
[ 0.61210000514984131, -0.35649999976158142, 0.70579999685287476, 0 ],
[ 0.27540001273155212, -0.74059998989105225, -0.6129000186920166, 0 ],
[ 1.9352999925613403, -1.0619000196456909, 38.685501098632813, 1 ]
],
"more": {}
}
]
})");
boost::property_tree::ptree pt;
read_json(iss, pt);
for (auto& [_,obj] : pt.get_child("objects")) {
std::cout << "Reading object " << std::quoted(obj.get<std::string>("name")) << "\n";
Array a;
Matrix m;
read(obj.get_child("array"), a);
read(obj.get_child("matrix"), m);
}
//write_json(std::cout, pt);
}
Prints:
Reading object "Some name"