Create array of json using boost property_tree
Finally I haven't found solution using boost library.
But this can be achieved by using cpprestsdk ("Casablanca").
Example:
using namespace web;
using namespace web::http;
using namespace web::http::client;
using namespace web::json;
void testFunction(http_request req)
{
// only to test the serialization of json arrays
json::value elmnt1;
elmnt1[L"element"] = json::value::string(U("value1"));
json::value elmnt2;
elmnt2[L"element"] = json::value::string(U("value2"));
json::value response; // the json array
response[0] = elmnt1;
response[1] = elmnt2;
string outputStr = utility::conversions::to_utf8string(shifts.serialize());
req.reply(200, outputStr, "application/json");
};
And this results in
[
{
"element":"value1"
},
{
"element":"value2"
}
]
JSON Array using Boost::Ptree
The link does answer it. All the answers clearly show that you should use push_back
(or insert
, actually), not put_child
.
You also have to read past the "how to make the array" and realize that you cannot have arrays as document root.
This is a symptom of the fact that Boost Ptree is not a JSON library. It's a Property Tree library, and it supports only property trees. The limitations are documented:
https://www.boost.org/doc/libs/1_74_0/doc/html/property_tree/parsers.html#property_tree.parsers.json_parser
DEMO
Here's the best you can do, assuming you did not really need the array as document root:
Live On Coliru
#define BOOST_BIND_GLOBAL_PLACEHOLDERS
#include <boost/property_tree/json_parser.hpp>
#include <iostream>
using boost::property_tree::ptree;
int main() {
ptree arr;
for (auto [k,v]: { std::pair
{"3", "SomeValue"},
{"40", "AnotherValue"},
{"23", "SomethingElse"},
{"9", "AnotherOne"},
{"1", "LastOne"} })
{
ptree element;
element.put(k, v);
arr.push_back({"", element});
}
// can't have array at root of doc...
ptree doc;
doc.put_child("arr", arr);
write_json(std::cout, doc);
}
Prints
{
"arr": [
{
"3": "SomeValue"
},
{
"40": "AnotherValue"
},
{
"23": "SomethingElse"
},
{
"9": "AnotherOne"
},
{
"1": "LastOne"
}
]
}
Json array creation using boost c++
Just to make it absolutely clear:
The documentation states that
The property tree dataset is not typed, and does not support arrays as such. Thus, the following JSON / property tree mapping is used [...]
It continues to describe that each ptree
represents a JSON object, always.
You need to remember that Boost Property Tree is not a JSON library. It is a Property Tree library, that optionally uses a subset of JSON for interoperability purposes. Therefore you cannot have arbitrary JSON things: You can not have a top-level arrays, you cannot have lone values, you cannot have actual numeric types, null, booleans etc.
You can only have Property Trees serialized using the described mappings.
Using boost property tree to read int array
JSON support, is spotty with boost property tree.
The property tree dataset is not typed, and does not support arrays as such. Thus, the following JSON / property tree mapping is used:
- JSON objects are mapped to nodes. Each property is a child node.
- 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.
- JSON values are mapped to nodes containing the value. However, all type information is lost; numbers, as well as the literals "null", "true" and "false" are simply mapped to their string form.
- Property tree nodes containing both child nodes and data cannot be mapped.
(from the documentation)
You can iterate the array with a helper function.
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
using boost::property_tree::ptree;
template <typename T>
std::vector<T> as_vector(ptree const& pt, ptree::key_type const& key)
{
std::vector<T> r;
for (auto& item : pt.get_child(key))
r.push_back(item.second.get_value<T>());
return r;
}
int main()
{
std::stringstream ss("{\"a\": [8, 6, 2], \"b\": [2, 2, 1]}");
ptree pt;
read_json(ss, pt);
for (auto i : as_vector<int>(pt, "a")) std::cout << i << ' ';
std::cout << '\n';
for (auto i : as_vector<int>(pt, "b")) std::cout << i << ' ';
}
See it Live On Coliru. Output:
8 6 2
2 2 1
How can I read an array of object from a JSON file using boost-property-tree in C++
In property_tree
arrays JSON are mapped into nodes (called just ptree
).
What is node/ptree ?
node/ptree {
data // has data
list < pair<key,node> > children // has children
}
In your input object you have property users with value as the array with 3 elements. These elements are mapped into three nodes with empty string as key.
So we have:
"users" node:
children {"",nodeA}, {"",nodeB}, {"",nodeC}
nodeA,B,C
represents elements of arrays. Each element of array is object with 2 properties. Objects like arrays are also mapped into nodes.
So nodeA
looks like:
nodeA:
children {"firstName",nodeD},{"lastName",nodeE} \\ as children we have 2 properties of object
and finally, nodeD
is
nodeD:
data = John
children emptyList
To get users property call get_child()
.
To iterate over all children use begin\end
methods on ptree
returned by get
. begin\end
returns iterator to pair with first
as key, and second
as nested ptree instance.
The below code iterates over elements in array:
boost::property_tree::ptree pt;
boost::property_tree::read_json("in.json",pt);
auto it = pt.get_child("users");
for (auto it2 = it.begin(); it2 != it.end(); ++it2)
{
for (auto it3 = it2->second.begin(); it3 != it2->second.end(); ++it3)
{
std::cout << it3->first; // [1]
std::cout << " : " << it3->second.data() << std::endl;
}
// [2]
std::cout << std::endl;
}
and prints:
firstName : John
lastName : Black
firstName : Kate
lastName : Red
firstName : Robin
lastName : White
in [1] line you should store firstName
/lastName
and in [2] line you could create new User
instance and push into vector.
Read a sub json using boost property tree
The body is not a string.
So, getting the child object would be in order:
for (auto const& v : jsonBatchResponse.get_child("responses")) {
std::string strID = v.second.get<std::string>("id", "");
std::string strStatus = v.second.get<std::string>("status", "");
ptree const& body = v.second.get_child("body");
}
If you add some output to that loop with e.g.
std::cout << std::quoted(strID) << "\n";
std::cout << std::quoted(strStatus) << "\n";
write_json(std::cout, body);
It will print Live On Coliru
"1"
"200"
{
"createdDateTime": "2021-04-22T09:24:59.394Z",
"displayName": "Test1",
"visibility": "public",
"isMembershipLimitedToOwners": "false",
"discoverySettings": {
"showInTeamsSearchAndSuggestions": "true"
},
"memberSettings": {
"allowCreateUpdateChannels": "true",
"allowCreateUpdateRemoveConnectors": "true"
},
"guestSettings": {
"allowCreateUpdateChannels": "true",
"allowDeleteChannels": "false"
},
"messagingSettings": {
"allowUserEditMessages": "true",
"allowChannelMentions": "true"
},
"funSettings": {
"allowGiphy": "true",
"allowCustomMemes": "true"
}
}
BONUS: Using a proper JSON library instead
Boost Property Tree is NOT a JSON library, and therefore has a lot of limitations.
Instead I suggest using Boost JSON:
Live On Coliru
#include <boost/json.hpp>
#include <boost/json/src.hpp>
#include <iostream>
namespace json = boost::json;
extern std::string sample;
int main() {
json::object jsonBatchResponse = json::parse(sample).as_object();
for (auto& v : jsonBatchResponse["responses"].as_array()) {
auto& res = v.as_object();
json::value id = res["id"], // string
status = res["status"], // integer
body = res["body"]; // object
std::cout << id << "\n";
std::cout << status << "\n";
std::cout << body << "\n";
}
}
std::string sample = R"(
{
"responses": [{
"id": "1",
"status": 200,
"headers": {
"OData-Version": "4.0",
"Content-Type": "application/json;odata.metadata=minimal;odata.streaming=true"
},
"body": {
"createdDateTime": "2021-04-22T09:24:59.394Z",
"displayName": "Test1",
"visibility": "public",
"isMembershipLimitedToOwners": false,
"discoverySettings": {
"showInTeamsSearchAndSuggestions": true
},
"memberSettings": {
"allowCreateUpdateChannels": true,
"allowCreateUpdateRemoveConnectors": true
},
"guestSettings": {
"allowCreateUpdateChannels": true,
"allowDeleteChannels": false
},
"messagingSettings": {
"allowUserEditMessages": true,
"allowChannelMentions": true
},
"funSettings": {
"allowGiphy": true,
"allowCustomMemes": true
}
}
}]
}
)";
Prints
"1"
200
{"createdDateTime":"2021-04-22T09:24:59.394Z","displayName":"Test1","visibility":"public","isMembershipLimitedToOwners":false,"discoverySettings":{"showInTeamsSearchAndSuggestions":true},"memberSettings":{"allowCreateUpdateChannels":true,"allowCreateUpdateRemoveConnectors":true},"guestSettings":{"allowCreateUpdateChannels":true,"allowDeleteChannels":false},"messagingSettings":{"allowUserEditMessages":true,"allowChannelMentions":true},"funSettings":{"allowGiphy":true,"allowCustomMemes":true}}
Why does Boost property tree write_json save everything as string? Is it possible to change that?
I ended up adding another function to my utils to solve this:
#include <string>
#include <regex>
#include <boost/property_tree/json_parser.hpp>
namespace bpt = boost::property_tree;
typedef bpt::ptree JSON;
namespace boost { namespace property_tree {
inline void write_jsonEx(const std::string & path, const JSON & ptree)
{
std::ostringstream oss;
bpt::write_json(oss, ptree);
std::regex reg("\\\"([0-9]+\\.{0,1}[0-9]*)\\\"");
std::string result = std::regex_replace(oss.str(), reg, "$1");
std::ofstream file;
file.open(path);
file << result;
file.close();
}
} }
Hope that helps.
Why this JSON string created by BOOST is different than the one required by my server?
Iterate the list to create an ptree array, as follows(If I understand correctly):
ptree pt_list;
for (auto &entry : AttendanceT_list) {
ptree pt;
pt.add("ADD", entry.Atten_Addr);
// ...
pt_list.push_back(std::make_pair("", pt));
}
ptree pt;
pt.push_back(std::make_pair("CommandString", pt_list));
// ...
write_json(os, pt, false);
see also: How to create an array using boost::property_tree?
Related Topics
C++ Virtual Override Functions with Same Name
Multiple Sfinae Class Template Specialisations Using Void_T
Std::Async Won't Spawn a New Thread When Return Value Is Not Stored
Skip Some Arguments in a C++ Function
Programmatically Getting System Boot Up Time in C++ (Windows)
Initializing a Ublas Vector from a C Array
C++ System() Function - How to Collect the Output of the Issued Command
Removing '#Include <Algorithm>' Doesn't Break the Code
How to Speed Up This Histogram of Lut Lookups
What Is Aggregate Initialization
Apply Function to All Eigen Matrix Element
Correct Format for Http Post Using Qnetworkrequest
How to Program for Windows Phone 7 in Standard C++ Only