How to use boost::property_tree to parse JSON with array root - c++

How can I get data from JSON with array as root node by using Boost.PropertyTree?
[
{
"ID": "cc7c3e83-9b94-4fb2-aaa3-9da458c976f7",
"Type": "VM"
}
]

The array elements are just values with a key named "" for property tree:
for (auto& array_element : pt) {
for (auto& property : array_element.second) {
std::cout << property.first << " = " << property.second.get_value<std::string>() << "\n";
}
}
Prints
ID = cc7c3e83-9b94-4fb2-aaa3-9da458c976f7
Type = VM
Live On Coliru
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <iostream>
using namespace boost::property_tree;
int main()
{
std::istringstream iss(R"([
{
"ID": "cc7c3e83-9b94-4fb2-aaa3-9da458c976f7",
"Type": "VM"
}
]
)");
ptree pt;
json_parser::read_json(iss, pt);
for (auto& array_element : pt) {
for (auto& property : array_element.second) {
std::cout << property.first << " = " << property.second.get_value<std::string>() << "\n";
}
}
}

Related

I can't with C++ boost property_tree parse nested key into JSON string

With a JSON string, I am able to get key's value from JSON without any nested keys:
std::string getFieldFromJson(std::string json, std::string field)
{
std::stringstream jsonEncoded(json); // string to stream
boost::property_tree::ptree root;
boost::property_tree::read_json(jsonEncoded, root);
if (root.empty())
return "";
return root.get <std::string>(field);
}
But I can't do it if I have a JSON like this:
{
"user": {
"id": 1,
"created_at": "2017-08-16T09:23:48.525+02:00",
......
}
For example, I want to read the value of key "created_at".
Using Boost JSON:
Live On Coliru
#include <boost/algorithm/string.hpp>
#include <boost/json.hpp>
#include <boost/json/src.hpp> // for header-only/COLIRU
#include <iostream>
namespace json = boost::json;
static std::string getFieldFromJson(std::string_view json,
std::string_view field) {
std::vector<std::string_view> tokens;
boost::algorithm::split(tokens, field, boost::algorithm::is_any_of("."));
auto el = boost::json::parse({json.data(), json.size()});
for (std::string_view token : tokens)
el = el.at({token.data(), token.size()});
return value_to<std::string>(el);
}
int main() {
auto input = R"(
{
"user": {
"id": 1,
"created_at": "2017-08-16T09:23:48.525+02:00",
"name": "John Doe"
}
})";
std::cout << getFieldFromJson(input, "user.created_at") << std::endl;
}
It throws an exception if anything goes wrong/isn't found
Improve The Interface
Instead of parsing each time, you'd probably parse once:
Live On Coliru
#include <boost/algorithm/string.hpp>
#include <boost/json.hpp>
#include <boost/json/src.hpp> // for header-only/COLIRU
#include <iostream>
namespace json = boost::json;
static json::value const& property(json::value const& root,
std::string_view expr) {
std::vector<std::string_view> tokens;
boost::algorithm::split(tokens, expr, boost::algorithm::is_any_of("."));
auto* cursor = &root;
for (std::string_view token : tokens)
cursor = &(cursor->at({token.data(), token.size()}));
return *cursor;
}
int main() {
auto doc = json::parse(R"( {
"user": {
"id": 1,
"created_at": "2017-08-16T09:23:48.525+02:00",
"name": "John Doe"
}
})");
std::cout << property(doc, "user.created_at") << std::endl;
}
Prints
"2017-08-16T09:23:48.525+02:00"
Even Better: Map Your Types
If you work with lost of data you can create mappers so you don't have to manually coerce the data types all the time:
Live On Coliru
#include <boost/algorithm/string.hpp>
#include <boost/date_time/local_time/local_time_io.hpp>
#include <boost/date_time/local_time/conversion.hpp>
#include <boost/json.hpp>
#include <boost/json/src.hpp> // for header-only/COLIRU
#include <iostream>
namespace json = boost::json;
using boost::local_time::local_date_time;
boost::local_time::time_zone_ptr
tz(new boost::local_time::posix_time_zone("MST-07:00:00"));
namespace boost::local_time {
static local_date_time tag_invoke(json::value_to_tag<local_date_time>, json::value const& v) {
std::stringstream iss;
iss.exceptions(std::ios::failbit | std::ios::badbit);
{
auto& s = v.as_string();
std::cout << "Trying: " << s << std::endl;
iss.write(s.data(), s.size());
}
local_date_time ldt(boost::date_time::not_a_date_time, tz);
iss.imbue(std::locale(
iss.getloc(), new local_time_input_facet("%Y-%m-%dT%H:%M:%S%Q")));
iss >> ldt;
return ldt;
}
}
struct User {
int64_t id;
local_date_time createdAt;
std::string name;
friend User tag_invoke(json::value_to_tag<User>, json::value const& v) {
auto& user = v.at("user");
std::cout << user << std::endl;
return {
user.at("id").as_int64(),
value_to<local_date_time>(user.at("created_at")),
value_to<std::string>(user.at("name")),
};
}
};
using Users = std::vector<User>;
int main() {
auto doc = json::parse(R"(
[
{
"user": {
"id": 1,
"created_at": "2017-08-16T09:23:48.525+02:00",
"name": "John Doe"
}
},
{
"user": {
"id": 2,
"created_at": "2022-04-23T14:56:16+00:00",
"name": "Jane Doe"
}
}
]
)");
auto users = value_to<Users>(doc);
auto now = boost::local_time::local_sec_clock::local_time(tz);
for (auto& [id, createdAt, name] : users) {
std::cout << "user #" << id << " " << std::quoted(name)
<< " was created " << (now - createdAt).total_seconds()
<< " seconds ago\n";
}
}
Prints e.g.
{"id":1,"created_at":"2017-08-16T09:23:48.525+02:00","name":"John Doe"}
Trying: "2017-08-16T09:23:48.525+02:00"
{"id":2,"created_at":"2022-04-23T14:56:16+00:00","name":"Jane Doe"}
Trying: "2022-04-23T14:56:16+00:00"
user #1 "John Doe" was created 147853388 seconds ago
user #2 "Jane Doe" was created 3040 seconds ago
Thanks. I also solved it with next:
std::string getFieldFromJson(std::string json, std::string field)
{
std::stringstream jsonEncoded(json); // string to stream convertion
boost::property_tree::ptree root;
boost::property_tree::read_json(jsonEncoded, root);
return (root.get<std::string>("user." + field));
}

access json array values in c++

I'm new to c++ and trying to use nlohmann library and I'm quite stuck. I want to modify array of objects from json.
json =
{
"a": "xxxx",
"b": [{
"c": "aaa",
"d": [{
"e": "yyy"
},
{
"e": "sss",
"f": "fff"
}
]
}]
}
now I want to replace e value with "example" in the above structure. Could some one help me.
I tried to loop through the json structure and was able to read the "e" value but can't replace it. I tried: `
std::vector<std::string> arr_value;
std::ifstream in("test.json");
json file = json::parse(in);
for (auto& td : file["b"])
for (auto& prop : td["d"])
arr_value.push_back(prop["e"]);
//std::cout<<"prop" <<prop["e"]<< std::endl;
for (const auto& x : arr_value)
std::cout <<"value in vector string= " <<x<< "\n";
for (decltype(arr_value.size()) i = 0; i <= arr_value.size() - 1; i++)
{
std::string s = arr_value[i]+ "emp";
std::cout <<"changed value= " <<s << std::endl;
json js ;
js = file;
std::ofstream out("test.json");
js["e"]= s;
out << std::setw(4) << js << std::endl;
}
The following appends MODIFIED to every "e"-value and writes the result to test_out.json:
#include <vector>
#include <iostream>
#include <fstream>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
int main() {
std::ifstream in("test.json");
json file = json::parse(in);
for (auto& td : file["b"])
for (auto& prop : td["d"]) {
prop["e"] = prop["e"].get<std::string>() + std::string(" MODIFIED");
}
std::ofstream out("test_out.json");
out << std::setw(4) << file << std::endl;
}
The prop["e"] = ... line does three things:
It gets the property with key "e",
Coerces it into a string using .get<std::string>() and appends "modified", and
writes back the result to prop["e"], which is a reference to the object nested in the JSON structure.

C++ nlohmann JSON get name of array

I have nlohmann json object:
json uuid = R"(
{
"uuid": ["aaa","bbb","ccc"]
}
)"_json;
I can get the values in array without problems: str = uuid["uuid"][0];
But how can i get array name himself ?
You can get the underlying map from the json object which gives you the array names and the arrays. If you just want to iterate through the items that's easy as well.
#include <iostream>
#include <json.hpp>
using json = nlohmann::json;
int main()
{
json uuid = R"(
{
"uuid": ["aaa","bbb","ccc"],
"uuie": ["aaa","bbb","ccc"],
"uuif": ["aaa","bbb","ccc"]
}
)"_json;
if (uuid.is_object())
{
auto obj = uuid.get<json::object_t>();
for (auto& kvp : obj)
{
std::cout << kvp.first << ":" << kvp.second << "\n";
}
}
for (auto& item : uuid)
{
std::cout << item << "\n";
}
return 0;
}

Reading Array of the json in JsonCpp

I tried to write a simple JSON reader for my program then I use JsonCpp. I have this JSON from my web server:
{
"return":
{
"status":200,
"message":"Accepted"
},
"entries":
[
{
"messageid":185002992,
"message":"CplusItsGood",
"status":1,
"statustext":"test",
"sender":"1234567",
"receptor":"123456789",
"date":1234,
"cost":140
}
]
}
And this is my C++ code:
Json::Reader reader;
Json::Value root;
reader.parse(jsonContext, root, false);
const Json::Value entriesArray = root["return"]["entries"];
int A = entriesArray["sender"].asInt();
cout << A;
It's print only 0, I can't read the sender or any other element of the entries array.
I want get the value of the cost or sender for example.
How can I do that?
your root contains 2 elements "return" and "entries" so or
root["return"] or root["entries"]
Then - array contains a list of members - so even if it only one entry - you still have to get it.
if value is quoted - it is string - you cannot use getInt on it. For example getInt is applicable to "status" not "sender"
Here is the whole sample
#include <iostream>
#include <string>
#include <json/json.h>
int main()
{
std::string s = R"({
"return":
{
"status":200,
"message":"Accepted"
},
"entries":
[
{
"messageid":185002992,
"message":"CplusItsGood",
"status":1,
"statustext":"test",
"sender":"1234567",
"receptor":"123456789",
"date":1234,
"cost":140
}
]
})";
Json::Reader reader;
Json::Value root;
reader.parse(s, root, false);
auto entriesArray = root["entries"];
auto firstelem = entriesArray[0];
std::string sender = firstelem["sender"].asString();
int i = std::stoi(sender);
std::cout << "array:" << entriesArray << "\n";
std::cout << "element:" << firstelem << "\n";
std::cout << "value:" << sender << "\n";
std::cout << "parsed value:" << i << "\n";
}
Output
array:[
{
"cost" : 140,
"date" : 1234,
"message" : "CplusItsGood",
"messageid" : 185002992,
"receptor" : "123456789",
"sender" : "1234567",
"status" : 1,
"statustext" : "test"
}
]
element:{
"cost" : 140,
"date" : 1234,
"message" : "CplusItsGood",
"messageid" : 185002992,
"receptor" : "123456789",
"sender" : "1234567",
"status" : 1,
"statustext" : "test"
}
value:1234567
parsed value:1234567
For arrays with only 1 object, use 0.
const Json::Value entriesArray = root["entries"][0];

Parse JSON array as std::string with Boost ptree

I have this code that I need to parse/or get the JSON array as std::string to be used in the app.
std::string ss = "{ \"id\" : \"123\", \"number\" : \"456\", \"stuff\" : [{ \"name\" : \"test\" }] }";
ptree pt2;
std::istringstream is(ss);
read_json(is, pt2);
std::string id = pt2.get<std::string>("id");
std::string num= pt2.get<std::string>("number");
std::string stuff = pt2.get<std::string>("stuff");
What is needed is the "stuff" to be retrieved like this as std::string [{ "name" : "test" }]
However the code above stuff is just returning empty string. What could be wrong
Arrays are represented as child nodes with many "" keys:
docs
JSON arrays are mapped to nodes. Each element is a child node with an empty name. If a node has both named and unnamed child nodes, it cannot be mapped to a JSON representation.
Live On Coliru
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
using boost::property_tree::ptree;
int main() {
std::string ss = "{ \"id\" : \"123\", \"number\" : \"456\", \"stuff\" : [{ \"name\" : \"test\" }, { \"name\" : \"some\" }, { \"name\" : \"stuffs\" }] }";
ptree pt;
std::istringstream is(ss);
read_json(is, pt);
std::cout << "id: " << pt.get<std::string>("id") << "\n";
std::cout << "number: " << pt.get<std::string>("number") << "\n";
for (auto& e : pt.get_child("stuff")) {
std::cout << "stuff name: " << e.second.get<std::string>("name") << "\n";
}
}
Prints
id: 123
number: 456
stuff name: test
stuff name: some
stuff name: stuffs
Since "stuff" is an array, you can iterate over the elements of it, which are dictionaries. And then you can iterate over the elements of the dictionary, which are key-value pairs:
for (const auto& dict : pt2.get_child("stuff")) {
for (const auto& kv : dict.second) {
std::cout << "key = " << kv.first << std::endl;
std::cout << "val = " << kv.second.get_value<std::string>() << std::endl;
}
}
continue with #sehe 's answer, related to the question asked by #xkm
Is it possible to get it like '[{ "name" : "some" }, { "name" : "stuffs" }]'
Yes, you can. Just treat it with an "unnamed" key which means the key with empty string.
f.g.
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <iostream>
using boost::property_tree::ptree;
int main()
{
std::stringstream ss;
ss << R"([{"a": 5}, {"a": 9}])";
ptree pt;
read_json(ss, pt);
for (auto& item : pt.get_child(""))
std::cout << "value is " << item.second.get<int>("a") << std::endl;
}