Creating JSON Arrays in Boost Using Property Trees

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

Sample Image

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



Leave a reply



Submit