I am using boost library to manipulate a JSON string and I would like to access to a first element.
I was wondering if there where some convenient way to access a first element of ptree with no path name.
I do this, but I got no value :
namespace pt = boost::property_tree;
pt::ptree pt2;
string json = "\"ok\"";
istringstream is(json);
try
{
pt::read_json(is, pt2);
cout << pt2.get_child("").equal_range("").first->first.data() << endl;
}
catch (std::exception const& e)
{
cerr << e.what() << endl;
}
Solution:
replace cout << pt2.get_child("").equal_range("").first->first.data() << endl;
by cout << pt2.get_value<std::string>() << endl;
Firstly, Property Tree is not a JSON library.
Secondly, the input is not in the subset of JSON supported by the library (e.g.).
Thirdly, since the input results in a tree that has no child nodes, you should use the value of the root node itself.
Lastly, if you had wanted the first node, use ordered_begin()->second:
Live On Coliru
#include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/xml_parser.hpp>
#include <iostream>
void broken_input() {
boost::property_tree::ptree pt;
std::istringstream is("\"ok\"");
read_json(is, pt);
std::cout << "Root value is " << pt.get_value<std::string>() << std::endl;
}
void normal_tree() {
boost::property_tree::ptree pt;
pt.put("first", "hello");
pt.put("second", "world");
pt.put("third", "bye");
std::cout << pt.ordered_begin()->second.get_value<std::string>() << std::endl;
write_json(std::cout, pt);
}
int main() {
try {
broken_input();
normal_tree();
}
catch (std::exception const& e)
{
std::cerr << e.what() << std::endl;
}
}
Prints
Root value is ok
hello
{
"first": "hello",
"second": "world",
"third": "bye"
}
I would like to access to a first element.
It is impossible in general case, since JSON elements are not place-fixed by definition. The current first element can change its place after JSON transformations and a resulting JSON will be the same, although elements are reordered. Thus such API is not provided by BOOST.
Related
I have this JSON:
[{"header": "test" , "test2" : "test2"}]
I'm trying to parse this using jsoncpp.
Here's my code snippet:
Json::CharReaderBuilder builder;
Json::CharReader *reader = builder.newCharReader();
Json::Value root;
bool parseRet = reader->parse(serverResponse.c_str(),serverResponse.c_str() +serverResponse.size(), &root, &errors);
parseRet returns true. But root does not include json data.
How do I parse this?
parseRet returns true. But root does not include JSON data.
This sounds like an issue with the way you're accessing the parsed JSON elements.
Below is a complete working example with a raw string literal as JSON input.
A few points:
Use std::unique_ptr for reader to de-allocate memory appropriately at the end. In your code snippet, it's a memory leak.
Read the documentation carefully! Accessing an element using a method or operator might return a default value or an exception so handle accordingly. For example, the JSON in root is an array so it should be accessed by index and then each index contains an object i.e. root[0]["header"].
Example (C++11):
#include <iostream>
#include <string>
#include <memory>
#include <jsoncpp/json/json.h>
int main()
{
const std::string raw_json = R"json([{"header": "test" , "test2" : "test2"}])json";
Json::CharReaderBuilder builder {};
// Don't leak memory! Use std::unique_ptr!
auto reader = std::unique_ptr<Json::CharReader>( builder.newCharReader() );
Json::Value root {};
std::string errors {};
const auto is_parsed = reader->parse( raw_json.c_str(),
raw_json.c_str() + raw_json.length(),
&root,
&errors );
if ( !is_parsed )
{
std::cerr << "ERROR: Could not parse! " << errors << '\n';
return -1;
}
std::cout << "Parsed JSON:\n" << root << "\n\n";
try
{
std::cout << "header: " << root[0]["header"] << '\n';
std::cout << "test2 : " << root[0]["test2"] << '\n';
}
catch ( const Json::Exception& e )
{
std::cerr << e.what() << '\n';
}
return 0;
}
Output:
Parsed JSON:
[
{
"header" : "test",
"test2" : "test2"
}
]
header: "test"
test2 : "test2"
I have a small but complex tree structure. Using, boost property tree as a container i am trying to iterate through the tree and subsequently emit it to the yaml file using yaml-cpp library.
For instance, i have a small nested property tree:
fibonacci:
type: series
entities:
golden_ratio:
ratio: 2.3
function:
power_series: 2
I want my yaml file to look exactly like this.
I wrote a recursive function to iterate through the tree and emit to yaml.
//Member variable
YAML::Emitter m_out
void iterator(const boost::property_tree::ptree& tree, const std::string& key)
{
for (const auto& item: tree)
{
if (item.second.data().empty()) //check if map node
{
m_out << YAML::BeginMap;
m_out << YAML::Key << item.first;
}
else if (!item.second.data().empty()) //else it is key/value pair
{
m_out << YAML::Key << item.first;
m_out << YAML::Value << item.second.data();
}
if (!item.second.empty()) //If the node has child
{
iterator(item.second, item.first);
}
}
}
I am calling the function with a emtpy key as iterator(root, ""). I know that the property tree works as key/value pairs, whereas, Yaml-cpp has node designations. In the code i am just trying to assume type of tree node based on value (no value - Map node, else - key/value node)
Apparently, my emitted yaml file doesn't possess the desired tree structure as presented above since my logic is wrong. I would like to make a recursive function which can iterate through any kind of tree and emit it to yaml file.
Is it possible to iterate tree and subsequently emit to yaml recursively? If yes, i would appreciate some ideas.
So I took your desired YAML and put it through an online converter to get a "reliable" ptree representation (which you interestingly left out of the question).
Then I proceeded to do a simple ptree roundtrip for sanity checks:
Live On Coliru
#include <boost/property_tree/json_parser.hpp>
#include <iostream>
using boost::property_tree::ptree;
std::istringstream sample_json();
ptree sample_ptree();
int main() {
write_json(std::cout, sample_ptree());
}
std::istringstream sample_json() {
return std::istringstream(R"({
"fibonacci": {
"type": "series",
"entities": {
"golden_ratio": {
"ratio": 2.3
},
"function": {
"power_series": 2
}
}
}
})");
}
ptree sample_ptree() {
ptree pt;
{
auto stream = sample_json();
read_json(stream, pt);
}
return pt;
}
Prints
{
"fibonacci": {
"type": "series",
"entities": {
"golden_ratio": {
"ratio": "2.3"
},
"function": {
"power_series": "2"
}
}
}
}
to_yaml take #1
The simplest is, of course, to read the same JSON, and let yaml-cpp do the conversion:
auto stream = sample_json();
std::cout << YAML::Load(stream) << "\n";
Prints:
{fibonacci: {type: series, entities: {golden_ratio: {ratio: 2.3}, function: {power_series: 2}}}}
to_yaml take #2: pretty print
First off
naming is important. iterator doesn't describe the function, and clashes with the well-known concept from the standard library
the key argument is unused
you only ever BeginMap, how are you expecting a valid tree if you don't have EndMap anywhere in the code?
No global variables please. They make your code brittle (non-deterministic, non-idempotent, non-reentrant, not threadsafe etc.). Just pass that Emitter& as a parameter.
I'd make it MUCH simpler:
void to_yaml(ptree const& node, YAML::Emitter &m_out) {
if (node.empty()) {
m_out << YAML::Value << node.data();
} else {
m_out << YAML::BeginMap;
for (auto const&item : node) {
m_out << YAML::Key << item.first;
to_yaml(item.second, m_out);
}
m_out << YAML::EndMap;
}
}
Now, to have a convenient entry point, add an overload:
std::string to_yaml(ptree const& tree) {
YAML::Emitter out;
to_yaml(tree, out);
return out.c_str();
}
Now you can print the result by doing:
std::cout << to_yaml(sample_ptree()) << "\n";
Prints:
fibonacci:
type: series
entities:
golden_ratio:
ratio: 2.3
function:
power_series: 2
Full Listing
#include <iostream>
#include <boost/property_tree/json_parser.hpp>
using boost::property_tree::ptree;
std::istringstream sample_json();
ptree sample_ptree();
#include "yaml-cpp/yaml.h"
void to_yaml(ptree const& node, YAML::Emitter &m_out) {
if (node.empty()) {
m_out << YAML::Value << node.data();
} else {
m_out << YAML::BeginMap;
for (auto const&item : node) {
m_out << YAML::Key << item.first;
to_yaml(item.second, m_out);
}
m_out << YAML::EndMap;
}
}
std::string to_yaml(ptree const& tree) {
YAML::Emitter out;
to_yaml(tree, out);
return out.c_str();
}
int main() {
write_json(std::cout, sample_ptree());
{
auto stream = sample_json();
std::cout << YAML::Load(stream) << "\n";
}
std::cout << to_yaml(sample_ptree()) << "\n";
}
std::istringstream sample_json() {
return std::istringstream(R"({
"fibonacci": {
"type": "series",
"entities": {
"golden_ratio": {
"ratio": 2.3
},
"function": {
"power_series": 2
}
}
}
})");
}
ptree sample_ptree() {
ptree pt;
{
auto stream = sample_json();
read_json(stream, pt);
}
return pt;
}
I have to write an XML parser with Boost. However I have some trouble.
I can access the nodes name without any problem, but for some reason I can't access the attributes inside a tag by using get_value, which should work instantly. Maybe there is a mistake in my code I didn't spot? Take a look:
void ParametersGroup::load(const boost::property_tree::ptree &pt)
{
using boost::property_tree::ptree;
BOOST_FOREACH(const ptree::value_type& v, pt)
{
name = v.second.get_value<std::string>("name");
std::string node_name = v.first;
if (node_name == "<xmlattr>" || node_name == "<xmlcomment>")
continue;
else if (node_name == "ParametersGroup")
sg.load(v.second); // Recursion to go deeper
else if (node_name == "Parameter")
{
// Do stuff
std::cout << "PARAMETER_ELEM" << std::endl;
std::cout << "name: " << name << std::endl;
std::cout << "node_name: " << node_name << std::endl << std::endl;
}
else
{
std::cerr << "FATAL ERROR: XML document contains a non-recognized element: " << node_name << std::endl;
exit(-1);
}
}
}
So basically I ignore and tags, when I'm in a ParametersGroup tag I go deeper, and when I'm in a Parameter tag I recover the datas to do stuff. However, I can't get the "name" properly.
This is the kind of lines I'm scanning in the last else if :
<Parameter name="box">
The std::cout << name displays things like that:
name: ^M
^M
^M
^M
^M
^M
which is obvisouly not what I'm asking for.
What am I doing wrong? Any help would be greatly appreciated.
Since your question isn't particularly selfcontained, here's my selfcontained counter example:
Live On Coliru
#include <sstream>
#include <iostream>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>
using namespace boost::property_tree;
int main() {
ptree pt;
std::istringstream iss("<Parameter name=\"box\" />");
xml_parser::read_xml(iss, pt);
for (auto& element : pt)
{
std::cout << "'" << element.first << "'\n";
for (auto& attr : element.second)
{
std::cout << "'" << attr.first << "'\n";
for (auto& which : attr.second)
{
std::cout << "'" << which.first << "': \"" << which.second.get_value<std::string>() << "\"\n";
}
}
}
}
It prints
'Parameter'
'<xmlattr>'
'name': "box"
I hope you can see what you need to do (likely an unexpected level of nodes in the tree?). To get directly to the leaf node:
pt.get_child("Parameter.<xmlattr>.name").get_value<std::string>()
I want to fill a boost::property_tree::ptree with the data from a xml,
the xml format is in a string which I passed to stringstream and then I try
to read it with read_xml, but the ptree data is null or empty when I look at the object
while debugging, my code:
std::stringstream ss;
ss << "<?xml ?><root><test /></root>";
boost::property_tree::ptree pt;
boost::property_tree::xml_parser::read_xml( ss, pt);
result:
pt {m_data="" m_children=0x001dd3b0 }
before I had a string with this xml code:
<?xml version="1.0"?><Response Location="910" RequesterId="12" SequenceNumber="0">
<Id>1</Id>
<Type>P</Type>
<StatusMessage></StatusMessage>
<Message>Error</Message>
</Response>
But nothing works using visual studio with c++.
There is no data associated with root node so m_data is empty but there is a child node (test) and m_children != nullptr.
Please consider this example:
#include <sstream>
#include <string>
#include <boost/property_tree/xml_parser.hpp>
int main()
{
std::stringstream ss;
ss << "<?xml ?><root><test /></root>";
boost::property_tree::ptree pt;
boost::property_tree::xml_parser::read_xml(ss, pt);
// There is no data associated with root node...
std::string s(pt.get<std::string>("root"));
std::cout << "EXAMPLE1" << std::endl << "Data associated with root node: " << s << std::endl;
// ...but there is a child node.
std::cout << "Children of root node: ";
for (auto r : pt.get_child("root"))
std::cout << r.first << std::endl;
std::cout << std::endl << std::endl;
std::stringstream ss2;
ss2 << "<?xml ?><root>dummy</root>";
boost::property_tree::xml_parser::read_xml(ss2, pt);
// This time we have a string associated with root node
std::string s2(pt.get<std::string>("root"));
std::cout << "EXAMPLE2" << std::endl << "Data associated with root node: " << s2 << std::endl;
return 0;
}
It'll print:
EXAMPLE1
Data associated with root node:
Children of root node: test
EXAMPLE2
Data associated with root node: dummy
(http://coliru.stacked-crooked.com/a/34a99abb0aca78f2).
The Boost propertytree library doesn’t fully document its capabilities, but a good guide for parsing XML with Boost is http://akrzemi1.wordpress.com/2011/07/13/parsing-xml-with-boost/
I am know approaching to boost property tree and saw that it is a good feature of boost libs for c++ programming.
Well, I have one doubt? how to iterate a property tree using iterators or similar?
In reference there is just an example of browsing the tree through:
BOOST_FOREACH
But is there nothing more? Something like an stl-like container? It would be a better solution, speaking about code quality....
Here is what I came up with after much experimentation. I wanted to share it in the community because I couldn't find what I wanted. Everybody seemed to just post the answer from the boost docs, which I found to be insufficient. Anyhow:
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <string>
#include <iostream>
using namespace std;
using boost::property_tree::ptree;
string indent(int level) {
string s;
for (int i=0; i<level; i++) s += " ";
return s;
}
void printTree (ptree &pt, int level) {
if (pt.empty()) {
cerr << "\""<< pt.data()<< "\"";
}
else {
if (level) cerr << endl;
cerr << indent(level) << "{" << endl;
for (ptree::iterator pos = pt.begin(); pos != pt.end();) {
cerr << indent(level+1) << "\"" << pos->first << "\": ";
printTree(pos->second, level + 1);
++pos;
if (pos != pt.end()) {
cerr << ",";
}
cerr << endl;
}
cerr << indent(level) << " }";
}
return;
}
int main(int, char*[]) {
// first, make a json file:
string tagfile = "testing2.pt";
ptree pt1;
pt1.put("object1.type","ASCII");
pt1.put("object2.type","INT64");
pt1.put("object3.type","DOUBLE");
pt1.put("object1.value","one");
pt1.put("object2.value","2");
pt1.put("object3.value","3.0");
write_json(tagfile, pt1);
ptree pt;
bool success = true;
try {
read_json(tagfile, pt);
printTree(pt, 0);
cerr << endl;
}catch(const json_parser_error &jpe){
//do error handling
success = false
}
return success;
}
Here is the output:
rcook#rzbeast (blockbuster): a.out
{
"object1":
{
"type": "ASCII",
"value": "one"
},
"object2":
{
"type": "INT64",
"value": "2"
},
"object3":
{
"type": "DOUBLE",
"value": "3.0"
}
}
rcook#rzbeast (blockbuster): cat testing2.pt
{
"object1":
{
"type": "ASCII",
"value": "one"
},
"object2":
{
"type": "INT64",
"value": "2"
},
"object3":
{
"type": "DOUBLE",
"value": "3.0"
}
}
BOOST_FOREACH is just a convenient way for iterating that can be done by iterator, begin() and end()
Your_tree_type::const_iterator end = tree.end();
for (your_tree_type::const_iterator it = tree.begin(); it != end; ++it)
...
And since C++11 it's:
for (auto& it: tree)
...
I ran into this issue recently and found the answers incomplete for my need, so I came up with this short and sweet snippet:
using boost::property_tree::ptree;
void parse_tree(const ptree& pt, std::string key)
{
std::string nkey;
if (!key.empty())
{
// The full-key/value pair for this node is
// key / pt.data()
// So do with it what you need
nkey = key + "."; // More work is involved if you use a different path separator
}
ptree::const_iterator end = pt.end();
for (ptree::const_iterator it = pt.begin(); it != end; ++it)
{
parse_tree(it->second, nkey + it->first);
}
}
Important to note is that any node, except the root node can contain data as well as child nodes. The if (!key.empty()) bit will get the data for all but the root node, we can also start building the path for the looping of the node's children if any.
You'd start the parsing by calling parse_tree(root_node, "") and of course you need to do something inside this function to make it worth doing.
If you are doing some parsing where you don't need the FULL path, simply remove the nkey variable and it's operations, and just pass it->first to the recursive function.
An addition to the answer How to iterate a boost property tree? :
In the C++11 style range based for for (auto node : tree), each node is a std::pair<key_type, property_tree>
Whereas in the manually written iteration
Your_tree_type::const_iterator end = tree.end();
for (your_tree_type::const_iterator it = tree.begin(); it != end; ++it)
...
the iterator it is a pointer to such a pair. It's a tiny difference in usage. For example, to access the key, one would write it->first but node.first.
Posted as a new answer, because my proposed edit to the original answer was rejected with the suggestion to post a new answer.
BFS based print ptree traversal, May be used if we want to do some algorithmic manipulation
int print_ptree_bfs(ptree &tree) {
try {
std::queue<ptree*> treeQ;
std::queue<string> strQ;
ptree* temp;
if (tree.empty())
cout << "\"" << tree.data() << "\"";
treeQ.push(&tree);
//cout << tree.data();
strQ.push(tree.data());
while (!treeQ.empty()) {
temp = treeQ.front();
treeQ.pop();
if (temp == NULL) {
cout << "Some thing is wrong" << std::endl;
break;
}
cout << "----- " << strQ.front() << "----- " << std::endl;
strQ.pop();
for (auto itr = temp->begin(); itr != temp->end(); itr++) {
if (!itr->second.empty()) {
//cout << itr->first << std::endl;
treeQ.push(&itr->second);
strQ.push(itr->first);
} else {
cout<<itr->first << " " << itr->second.data() << std::endl;
}
}
cout << std::endl;
}
} catch (std::exception const& ex) {
cout << ex.what() << std::endl;
}
return EXIT_SUCCESS;
}